Olá pessoal,
Neste artigo eu descrevo o módulo de DMA disponível em todos os modelos RL78/G13. O DMA (Direct Memory Access ou acesso direto à memória) é um módulo que permite que um periférico acesse diretamente a memória RAM do microcontrolador, transferindo dados da RAM para o periférico ou vice-versa.
A utilidade do DMA é indiscutível, mas é conveniente exemplificar como este periférico pode auxiliar numa aplicação.
As aplicações envolvendo microcontroladores frequentemente possuem a necessidade de movimentar dados: sejam caracteres recebidos ou a serem enviados pela porta serial, sejam amostras obtidas pelo conversor A/D, etc. Tradicionalmente utilizam-se interrupções para sinalizar a CPU que existe a necessidade de se realizar uma movimentação de dados como as descritas. Assim, ao receber um novo caractere, por exemplo, a UART gera uma interrupção. A CPU reconhece a interrupção e desvia o fluxo do programa para a ISR que irá efetuar a leitura do dado recebido e o seu armazenamento, por exemplo, num buffer.
Esta abordagem, bastante comum, apresenta um grande inconveniente: ela consome um tempo precioso da CPU e o utiliza pra uma tarefa que, digamos, é pouco nobre. É aí que entra o módulo de acesso direto à memória (DMA), pois ele permite automatizar este tipo de operação, realizando a transferência de dados entre periférico e memória sem a necessidade de intervenção da CPU!
Nos RL78, o módulo de DMA apresenta-se com 2 ou 4 canais independentes. Cada canal pode ser configurado para realizar transferências de 8 ou 16 bits entre periféricos e RAM (a direção pode ser selecionada), com blocos de até 1024 transferências.
Um canal de DMA pode ser disparado por um dos seguintes periféricos: conversor A/D, canais da TAU, SAU (UART) ou SAU (CSI). Uma característica interessante é que uma transferência DMA pode ser disparada por um periférico, mas a operação de transferência do DMA pode envolver outro periférico e não necessariamente aquele que provocou o disparo. Um exemplo típico desta situação é a utilização de um canal da TAU para realizar leituras periódicas de uma porta de E/S e o seu armazenamento (através do DMA) na RAM.
O exemplo a seguir demonstra como utilizar um canal de DMA para automatizar a transmissão de dados com a UART.
Um buffer de 32 bytes é utilizado para o armazenamento dos dados a serem transmitidos. A função tx_UART2() é utilizada para escrever uma string no buffer e configurar o DMA para realizar a transferência dos dados através da UART2.
Toda a transferência é realizada automaticamente pelo DMA, ou seja, após cada caractere ser transmitido o flag de transmissão da UART2 (STIF2) é setado, provocando o disparo do DMA que transfere um novo caractere do buffer para o registrador TXD2.
Este processo segue até que todos os caracteres tenham sido transmitidos (registrador DBC0 chegue a zero e bit DST seja apagado no registrador DRC0).
Adicionalmente a aplicação também responde aos caracteres recebidos pela UART2, invertendo o estado do led D2 da placa RPB a cada caractere recebido e enviando a string “Teste!” ao receber o caractere ‘t’ e “RL78!” ao receber o caractere ‘r’.
O código foi escrito para a placa de promoção do RL78/G13 (YRPBRL78G13) e utiliza a porta COM virtual disponibilizada pela mesma. Para utilizar esta porta virtual, basta configurar, após o download do código, os jumpers J6 a J9 na posição 2-3.
#include "ior5f100le.h"
#include "ior5f100le_ext.h"
#include "intrinsics.h"
#include "myRL78.h"
// Configura watchdog = desligado
#pragma location = "OPTBYTE"
__root __far const char opbyte0 = WDT_OFF;
// Configura detector de baixa tensão = desligado
#pragma location = "OPTBYTE"
__root __far const char opbyte1 = LVD_OFF;
// oscilador 32MHz flash high speed
#pragma location = "OPTBYTE"
__root __far const char opbyte2 = FLASH_HS | CLK_32MHZ;
// debug ativado, com apagamento em caso de falha de autenticação
#pragma location = "OPTBYTE"
__root __far const char opbyte3 = DEBUG_ON_ERASE;
/* Configura security ID */
#pragma location = "SECUID"
__root __far const char senha[10] = {0,0,0,0,0,0,0,0,0,0};
#define __9600BPS 51<<9
#define LED P7_bit.no7
#define TX_BUF_SIZE 32
unsigned char tx_buffer[TX_BUF_SIZE];
void tx_UART2(unsigned char *string);
#pragma vector = INTSR2_vect
__interrupt void trata_rx_UART2(void)
{
unsigned char temp;
temp = RXD2; // lê o caractere recebido
// se recebeu 't' envia "Teste!"
if (temp=='t') tx_UART2("Teste!\r\n");
// se recebeu 'r' envia "RL78!"
if (temp=='r') tx_UART2("RL78!\r\n");
LED = !LED; // a cada caractere recebido, inverte o estado do led
}
#pragma vector = INTDMA0_vect
__interrupt void trata_DMA_tx_uart2(void)
{
DEN0 = 0; // desabilita o DMA
}
void MCU_init(void)
{
PM1_bit.no3 = 0; // P13/TXD2 como saída
P1_bit.no3 = 1; // coloca TXD2 em 1 (importante!!!)
PM1_bit.no4 = 1; // P14/RXD2 como entrada
PM7_bit.no7 = 0; // P77 como saída (led)
LED = 1; // desliga o led
SAU1EN = 1; // ativa a SAU1
// Clock CK0 da SAU1 = 32MHz / 32 = 1MHz
SPS1 = SAU_CK0_DIV32;
// Configura o canal 0 da SAU1 (transmissão da UART2)
SMR10 = SAU_MD_UART;
SCR10 = SAU_COMM_TX|SAU_NO_PARITY|SAU_LSB_FIRST|SAU_ONE_STOP|SAU_8BITS;
SDR10 = __9600BPS; // seta o baud rate do transmissor
// Configura o canal 1 da SAU1 (recepção da UART2)
SMR11 = bSAU_STS | SAU_MD_UART;
SCR11 = SAU_COMM_RX|SAU_NO_PARITY|SAU_LSB_FIRST|SAU_ONE_STOP|SAU_8BITS;
SDR11 = __9600BPS; // seta o baud rate do receptor
SOE1 = SAU_CH0; // habilita a saída da UART2
SO1 = SAU_CH0; // seta a saída TXD2
NFEN0 = SNFEN20; // ativa o filtro digital da entrada RXD2
// Dispara os canais 0 e 1 da SAU1
SS1 = SAU_CH1 | SAU_CH0;
SRMK2 = 0; // habilita a interrupção de recepção da UART
DMAMK0 = 0; // habilita a interrupção do DMA0
__enable_interrupt(); // habilita as interrupções do RL78
}
// envia uma string pela UART2 utilizando o DMA
void tx_UART2(unsigned char *string)
{
unsigned char index;
index = 0;
// copia a string para o buffer de transmissão
while (string)
{
tx_buffer[index++] = *string++;
// se atingiu o final da string ou o tamanho do buffer, encerra a cópia
if (index==TX_BUF_SIZE || !*string) break;
}
// se o buffer contém caracteres, configura o DMA para a transferência
if (index)
{
DRC0 = bDEN; // habilita o DMA0
DBC0 = index; // configura o número de bytes a transferir
DSA0 = (char)&TXD2; // configura o endereço do registrador alvo
DRA0 = (int)tx_buffer; // configura o endereço do buffer com os dados
DMC0 = bDRS | DMA_TRIG_TX2; // configura o canal de DMA
DRC0 = bDEN | bDST; // seta DST para iniciar a transferência
STG0 = 1; // dispara o DMA e inicia a transferência
}
}
void main(void)
{
MCU_init();
// transmite uma string de saudação
tx_UART2("Teste da UART com DMA!");
while (1); // aguarda uma interrupção
}

Olá Fábio. Estou seguindo aqui o livro e já no primeiro exemplo travei.
O IAR não esta recnhecendo o #include “myRL78.h” e nem a última palavra das linhas com “__root __far” que vem logo abaixo. Pode me dar essa força?
Olá Rafael,
Você baixou o código daqui do blog? O arquivo myRL78.h é de minha autoria e deve ser baixado juntamente com os demais arquivos do livro, conforme está mencionado na página 40. Qualquer problema estou as ordens.
Fábio
Ok Fábio. Já baixei e estou avançando no livro. Obrigado.
@Fábio