Собственно мои небольшие заметки на полях о работе с USART. В сети итак полно всякого, это просто заметка для себя. При учете того что я не пользовался ни AVR studio, ни плагином для Eclipse.
Table of Contents
Тестовый пример
Ведение AVR проектов в Emacs
Используя Emacs хотелось бы удобной а главное безвылазной работы в нем. И вообще настроить Emacs как IDE, там автодополнение, описание функций, сборка. Конечно при упоминании С и Emacs поиск выдаст CEDET а в качестве менеджера проектов встроенный в CEDET — EDE. Но к сожалению поиски живого носителя знаний о EDE и его настройке, пока не увенчались успехом.
возьмем Makefile из сети.
## ## This file is part of the avr-gcc-examples project. ## ## Copyright (C) 2008 Uwe Hermann <uwe@hermann-uwe.de> ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ## PROGRAM = dhltterm_atmega16 MCU = atmega16 CC = avr-gcc OBJCOPY = avr-objcopy CFLAGS += -Wall -g -Os -mmcu=$(MCU) LDFLAGS += OBJS = $(PROGRAM).o # Be silent per default, but 'make V=1' will show all compiler calls. ifneq ($(V),1) Q := @ endif all: $(PROGRAM).hex $(PROGRAM).elf: $(PROGRAM).o @printf " LD $(subst $(shell pwd)/,,$(@))\n" $(Q)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(PROGRAM).hex: $(PROGRAM).elf @printf " OBJCOPY $(subst $(shell pwd)/,,$(@))\n" $(Q)$(OBJCOPY) -O ihex $< $@ %.o: %.c @printf " CC $(subst $(shell pwd)/,,$(@))\n" $(Q)$(CC) $(CFLAGS) -o $@ -c $< # Моя кривая добавка, для генерации ассемблерного кода disasm: $(PROGRAM).c @printf " DASM $(PROGRAM).s\n" $(Q)$(CC) $(CFLAGS) -S -o $(PROGRAM).s $< flash: $(PROGRAM).hex @printf " FLASH $(PROGRAM).hex\n" $(Q)avrdude -c avrispv2 -P usb -p t13 -U flash:w:$(PROGRAM).hex clean: @printf " CLEAN $(subst $(shell pwd)/,,$(OBJS))\n" $(Q)rm -f $(OBJS) @printf " CLEAN $(PROGRAM).elf\n" $(Q)rm -f *.elf @printf " CLEAN $(PROGRAM).hex\n" $(Q)rm -f *.hex @printf " CLEAN $(PROGRAM).s\n" $(Q)rm -f *.s
Отдельная беда в том что вместе с avr-libc устанавливается вся необходимая документация и примеры, но прочесть их нельзя, а на вопросы вроде
man -k avr-libc avr-man -k avr-libc info -k avr-libc #Небольшой пример man assembler
Но это не дело не качать же все подряд. Есть вариант открыть PDF, например так.
evince /usr/share/doc/avr-libc/avr-libc-user-manual.pdf.gz
или открыть в браузере file:///usr/share/doc/avr-libc/avr-libc-user-manual/index.html
Тестовый пример
Есть куча прекрасных статей описывающих работу UART ,в блоге easyelectronics —вот этот хороший но он для асма и я прошел мимо, потому как хотелось сразу в С да по максимуму и тоже было,но наворочено. В общем я побоялся очередной AVRStudio магии и не захотел её ставить.
Берем некий тестовый проект.
define F_CPU 8000000UL // 8 MHz /*Very Important - change F_CPU to match target clock Note: default AVR CLKSEL is 1MHz internal RC This program transmits continously on USART. Interrupt is used for Receive charactor, which is then transmitted instead. LEDs are used as a test. Normal RX routine is included but not used. Change USART_BAUDRATE constant to change Baud Rate */ #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> // Define baud rate #define USART_BAUDRATE 9600 #define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1) volatile unsigned char value; /* This variable is volatile so both main and RX interrupt can use it. It could also be a uint8_t type */ /* Interrupt Service Routine for Receive Complete NOTE: vector name changes with different AVRs see AVRStudio - Help - AVR-Libc reference - Library Reference - <avr/interrupt.h>: Interrupts for vector names other than USART_RXC_vect for ATmega32 */ ISR(USART_RXC_vect){ value = UDR; //read UART register into value PORTB = ~value; // output inverted value on LEDs (0=on) } void USART_Init(void){ // Set baud rate UBRRL = BAUD_PRESCALE;// Load lower 8-bits into the low byte of the UBRR register UBRRH = (BAUD_PRESCALE >> 8); /* Load upper 8-bits into the high byte of the UBRR register Default frame format is 8 data bits, no parity, 1 stop bit to change use UCSRC, see AVR datasheet*/ // Enable receiver and transmitter and receive complete interrupt UCSRB = ((1<<TXEN)|(1<<RXEN) | (1<<RXCIE)); } void USART_SendByte(uint8_t u8Data){ // Wait until last byte as been transmitted while((UCSRA &(1<<UDRE)) == 0); // Transmit data UDR = u8Data; } // not being used but here for completeness // Wait until a byte has been received and return received data uint8_t USART_ReceiveByte(){ while((UCSRA &(1<<RXC)) == 0); return UDR; } void Led_init(void){ //outputs, all off DDRB =0xFF; PORTB = 0xFF; } int main(void){ USART_Init(); // Initialise USART sei(); // enable all interrupts Led_init(); // init LEDs for testing value = 'A'; //0x41; //PORTB = 0b11101110; // 0 = LED on for(;;){ // Repeat indefinitely USART_SendByte(value); // send value _delay_ms(250); // delay just to stop Hyperterminal screen // cluttering up } }
Разбор переменных
define F_CPU 8000000UL // 8 MHz #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> // Define baud rate #define USART_BAUDRATE 9600 #define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
BAUDRATE — количество битов передачи в одной секунде
BAUD_PRESCALE — у микроконтроллера есть собственная частота работы F_CPU, передача данных по USART происходит на другой частоте BAUDRATE так как физически устройство использует частоту F_CPU, а для отсчета одного такта передачи UART внутренний счетчик сверяется с числом записанным в регистры UBRR
UBRR — USART Baud Rate register — так как число большое оно разбито на два регистра
таблица 18-1 datasheet поясняет формулу используемую для расчета PRESCALE
Вообще … посмотрел статью задумался о надобности своей
Асинхронный режим работы
[/lаtex]
Асинхронный режим работы на двойной скорости
[/lаtex]
Синхронный режим мастера
[/lаtex]
По этому мало информации, вот из русскоязычного. Спасибо чуваку(прикрути комменты если прочтешь).
Продолжаем исследования.
UCSR1B = ((1<<TXEN1)|(1<<RXEN1) | (1<<RXCIE1));
- UCSR1B USART Control and Status Register n B
- TXEN1 — стр 207 Transmitter Enable включить передатчик
- RXEN1 — Reciever Enavble ыключить приемник
- RXCIE1 -RXCIEn: RX Complete Interrupt Enable -разрешить прерывание
итого эта строчка на самом деле заменяется препроцессором
UCSR1B = ((1<<TXEN1)|(1<<RXEN1) | (1<<RXCIE1)); _SFR_MEM8(0xC9) =( (0b00000001<<3)| (0b00000001<<4)| (0b00000001<<7)) _SFR_MEM8(0xC9) =( (0b00001000)|(0b00010000)|(0b10000000)) _SFR_MEM8(0xC9) =(0b10011000)
итак мы просто записали единицы в нужные биты.
Поэтому со стандартными макросами avr-libc нельзя писать присвоение какой-то конкретной ноги/бита
// Хотим присвоить 1 одному биту не трогая другие в регистре PORTB3 = 1 // так нельзя PORTB = PORTB | (1<<PORTB3) // | - побитовое ИЛИ PORTB |= (1<<PORTB3) //тоже самое //Хотим присвоить 0 одному биту не трогая другие в регистре PORTB3 = 0 // так нельзя PORTB &= ~(1<<PORTB3) PORTB = PORTB & ( ~(0b00001000) ) //~ - логическое НЕ PORTB = PORTB & ( 0b11110111 ) // & - побитовое И
Не считаю это удобным но пока не в курсе где правильней расположить собственные макросы.
Так теперь рассмотрим
ISR(USART_RXC_vect){ value = UDR1; //read UART register into value PORTB = ~value; // output inverted value on LEDs (0=on) } // Компилятор ругнется //‘USART_RXC_vect’ appears to be a misspelled // signal handler [enabled by default] // пока я слабо понимаю в чем переносимость кода // если посмотреть в заголовочном файле микроконтроллера // напоминаю /usr/lib/avr/include/avr/ // то правильней USART1_RX_vect
собственно функция читает пришедший на регистр байт и пишет его в регистр вывода PORTB .. т.е. зажигает диоды
теперь этот кусок
void USART_SendByte(uint8_t u8Data){ // Wait until last byte as been transmitted while((UCSR1A &(1<<UDRE1)) == 0); // Transmit data UDR1 = u8Data; }
- UCSR1A — USART Control and Status Register A
- UDRE1 — UDRIEn: USART Data Register Empty Interrupt Enable n
Writing this bit to one enables interrupt on the UDREn Flag. A Data Register Empty interrupt will be generated only if the UDRIEn bit is written to one, the Global Interrupt Flag in SREG is written to one and the UDREn bit in UCSRnA is set.
Ждем когда регистр с данными станет пустым и бит UDRE1 установится в 1 затем пишем новые данные
Получение данных
uint8_t USART_ReceiveByte(){ while((UCSR1A &(1<<RXC1)) == 0); return UDR1; }
RXC1 – RXCn: USART Receive Complete This flag bit is set when there are unread data in the receive buffer and cleared when the receive buffer is empty (i.e., does not contain any unread data)
Ждем пока по каналу передадутся все биты данных (до стоп бита)после этого RXC1 устанавливается в 1 и мы считываем наш байт
Теоретически эта программа должна заполнить терминал одни символом А
Добавить комментарий