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