We implement the similarity of DMA on AVR microcontrollers

Hello. I had a chance to write a rather large project on AVRs. As you know, they have not very high speeds of 16 MHz, the same STM32 can drive 72 MHz and higher. But there is little experience on STM, so far AVR. So I needed to transfer data via UART in the project, the parcels are not large 10-15 bytes, the speed is 9600. If this whole thing is implemented in the main loop, then this slows down the system very much. And besides this, I have a bunch of other tasks. The only way out is to use interrupts. I looked at several examples on the Internet, some of them are complex, others I didn’t even understand how they work, and as a result I did as I understood, and now I’m sharing with you.

And so, first of all, we need to form what we want to send, that is, get a buffer to send, no matter how sprintf or itoa. Let’s define a buffer:

char sprintf_buf [15];

Now we create another buffer from which data will be sent to the UART, since the data in the first buffer can change at an arbitrary point in time, and this can change the data to be sent.

char uart0_tx_buf [20]; 

Now let’s write a function that will rewrite data from one buffer to another. There is a point here, if you use strcpy, then when copying a string to the buffer, be sure to add 0 at the end, which will indicate that this is the last character. But if your data in the buffer can theoretically store the number 0 not in character form, then it is better to use a special character. I chose to use ‘$’. The buffer will overwrite until it encounters a ‘$’ character and stop immediately. The function looks like this:

void uart0_send_string ( char *send0)                // сюда передаем строку в конце которой символ $
     if (uart0_tx_caunter == 0)                     // если счетчик на нуле можем обновить буфер, если нет то пропускаем
          for (int i = 0; i < 20; i ++)             // забиваем буффер отправки символами из переданого буфера
               uart0_tx_buf[i] = send0[i];
               if (uart0_tx_buf[i] == '$') break;   // пока не встретим символ $. но его мы тоже заносим
          UDR0 = uart0_tx_buf[uart0_tx_caunter];    // отправляем нулевой байт UART 
          uart0_tx_caunter ++;                      // увеличиваем счетчик
          UCSR0B |= ( 1<<UDRIE0);                   // включаем прерывание по опустрошению буффера передачи

Briefly what is happening here. Suppose we have already filled our sprintf_buf transfer buffer and now we call the uart0_send_string ( sprintf_buf ) function, passing our buffer to it. As you may have noticed, there is another counter: uart0_tx_caunter. It is needed for several tasks, the first if we try to send data until the UART has completed the transfer, we will not succeed, because if (uart0_tx_caunter == 0) will stupidly not let us in. And secondly, this counter allows the UART to know which buffer element to send.

I also forgot to talk about the main thing, namely the initialization of the UART. I will not paint much, it is assumed that you know how to adjust the speed, interrupts, and just turn on:

void init_Uart_0 (void)
     UCSR0B=(1<<RXEN0)|( 1<<TXEN0);                  //Включаем прием и передачу по USART
     UCSR0B |= (1<<RXCIE0);                          //Разрешаем прерывание при приеме
     UCSR0C = (1<<UPM01)|(1<<UCSZ01)|(1<<UCSZ00);    //паритет 1, 8 бит 

As you can see, the interrupt during transmission (or rather, by emptying the transmission buffer UCSR0B |= ( 1<

//----------обработчики прерываний на передачу ----------//
ISR (USART0_UDRE_vect)                                      // прерывание по опустошению буффера передачи
     if(uart0_tx_buf[uart0_tx_caunter] != '$')              // пока символ в буффере не равен $
          UDR0 = uart0_tx_buf[uart0_tx_caunter];            // продолжаем копировать из буффера байты в юарт
          uart0_tx_caunter ++;                              // ну и естественно передвигаемся по массиву
     else                                                   // если дошли до символа $
          uart0_tx_caunter = 0;                             // сбрасываем счетчик
          UCSR0B  &= ~ ( 1<<UDRIE0);                        // вырубаем прерывания по опустошению буфера

Here the logic is similar, we went into interrupts, checked the character, if it’s not ‘$’ then we write the character, increase the counter and exit the interrupt again. We again have a lot of free time to do our own thing. And so it will be until the interrupt meets ‘$’. Then we reset the counter, turn off the interrupt and are ready to wait for a new package.

What gives ‘$’, it allows you to send parcels of different lengths (the main thing is that it fits into the buffer). If the package is fixed, then you can make it even easier, but I made a universal code.

In general, do not kick much, this is my first post. Constructive criticism welcome 🙂

Similar Posts

Leave a Reply