У функций для приёма/передачи данных по uart/usart в avr atmega, описанных в предыдущей заметке есть существенный недостаток, они работают по опросу статусного бита, а это значит, что программа микроконтроллера часто будет крутиться в цикле опроса, вместо того чтобы выполнять какие-нибудь другие полезные действия.
Что бы освободить программу контроллера от этих рутинных действий будем использовать циклический буфер и прерывания. Для реализации циклического буфера возьмем код из заметки простое FIFO.
В контроллере uart/usart avr atmega реализованы три вектора прерывания:
- по окончанию передачи байта
- по окончанию приёма байта
- по освобождению регистра данных передатчика
Будем использовать последние два. По прерыванию приёма будем складывать принятый байт в один циклический буфер (rx_fifo), а по освобождению регистра данных передатчика передавать байт, извлеченный из другого циклического буфера (tx_fifo). Основная программа будет считывать данные из rx_fifo а записывать в tx_fifo.
При настройке uart/usart в avr atmega разрешаем прерывание по приёму байта:
//разрешить прием, передачу данных и прерывание по приёму байта UCSRB = ( 1 << TXEN ) | ( 1 << RXEN ) | (1 << RXCIE ); |
Пример обработчика прерывания по приёму байта в uart/usart avr atmega:
ISR( USART_RXC_vect ) { unsigned char rxbyte = UDR; if( !FIFO_IS_FULL( uart_rx_fifo ) ) { FIFO_PUSH( uart_rx_fifo, rxbyte ); } } |
Пример обработчика прерывания по освобождению регистра данных передатчика в uart/usart avr atmega:
ISR( USART_UDRE_vect ) { if( FIFO_IS_EMPTY( uart_tx_fifo ) ) { //если данных в fifo больше нет то запрещаем это прерывание UCSRB &= ~( 1 << UDRIE ); } else { //иначе передаем следующий байт char txbyte = FIFO_FRONT( uart_tx_fifo ); FIFO_POP( uart_tx_fifo ); UDR = txbyte; } } |
Функции записывающие и считывающие данные в fifo, оформим так, что бы их можно было использовать для создания стандартного потока данных. Это позволит использовать стандартные функции, такие как putchar, getchar, gets, puts, printf, scanf. Так как c fifo мы работаем и в прерываниях, что бы не было сбоев, будем запрещать прерывания при записи и извлечении данных из циклических буферов.
int uart_putc( char c, FILE *file ) { int ret; cli(); //запрещаем прерывания if( !FIFO_IS_FULL( uart_tx_fifo ) ) { //если в буфере есть место, то добавляем туда байт FIFO_PUSH( uart_tx_fifo, c ); //и разрешаем прерывание по освобождению передатчика UCSRB |= ( 1 << UDRIE ); ret = 0; } else { ret = -1; //буфер переполнен } sei(); //разрешаем прерывания return ret; } |
int uart_getc( FILE* file ) { int ret; cli(); //запрещаем прерывания if( !FIFO_IS_EMPTY( uart_rx_fifo ) ) { //если в буфере есть данные, то извлекаем их ret = FIFO_FRONT( uart_rx_fifo ); FIFO_POP( uart_rx_fifo ); } else { ret = _FDEV_EOF; //данных нет } sei(); //разрешаем прерывания return ret; } |
Ну и под конец пример программы:
FILE uart_stream = FDEV_SETUP_STREAM(uart_putc, uart_getc, _FDEV_SETUP_RW); int main( void ) { stdout = stdin = &uart_stream; uart_init(); sei(); puts( "Hello world\r\n" ); while( 1 ) { int c = getchar(); if( c != EOF ) { putchar( c ); } } return 0; } |
Исходники примера работы с uart для avr-gcc можно скачать тут
Отправил вам письмо.
Пишите, буду рад помочь :)
к сожалению на сайте нет ваших контактов, мне нужна консультация по програмированию atmega 1280 и возможно заказ программы,не могли бы вы связаться со мной или оставить свои координаты для связи
Я немного изменил для себя функцию uart_getc. Теперь эта функция при отсутствии символов в буфере чтения uart_rx_fifo будет ждать, пока в буфере не появится хоть один байт. Однако микроконтроллер «зависает», как определил позже, не завершается цикл while( FIFO_IS_EMPTY( uart_rx_fifo ) ). текст модифицированной функции привожу ниже.
Вы не могли бы подсказать, почему нет выхода из цикла при приеме байта?
Попробуйте изменить описание структуры FIFO
#define FIFO( size ) struct { unsigned char buf[size]; volatile unsigned char tail; volatile unsigned char head; }
Про volatile можно почитать тут
Цитата: «Функции записывающие и считывающие данные в fifo, оформим так, что бы их можно было использовать для создания стандартного потока данных. Это позволит использовать стандартные функции, такие как putchar, getchar, gets, puts, printf, scanf.»
Вопрос по работе функции gets(s); Как она должна работать в данном случае? Ведь по UART передаётся информация байтами. И не обязательно последним байтом будет код конца строки. Во многих случая будет \n или \r. Или функция gets(s); не работает? Тогда нужно писать свой обработчик? Кстати, функция puts(s); вроде как работает и передаёт строку по UART.
Как точно будет работать gets сказать не могу, надо смотреть как именно она реализована в avr-libc.
Но по стандарту gets обрабатывает ‘\n’ и признак конца файла, при этом заменяет их на ‘\0′
В примере признак конца файла ( _FDEV_EOF ) возвращается функцией uart_getc когда в буфере кончились данные FIFO_IS_EMPTY( uart_rx_fifo ),
поэтому gets будет работать если строка отправлена без пауз между байтами (как например при ручном вводе), т.е. пока обрабатываем один принятый байт, должен уже прийти следующий.
Но лучше написать свой обработчик, т.к. в gets нет проверки выхода за пределы массива.
Пример обработчика можно посмотреть в заметке про подключение gps приёмника http://mainloop.ru/avr-atmega/avr-gps.html