Собственно мои небольшие заметки на полях о работе с USART. В сети итак полно всякого, это просто заметка для себя. При учете того что я не пользовался ни AVR studio, ни плагином для Eclipse.

Тестовый пример

Ведение 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] \begin{eqnarray} BAUD = \frac{F_{OSC}}{16(UBRRn+1)} \\ UBRR = \frac{F_{OSC}}{16BAUD}-1 \end{eqnarray}
[/lаtex]

Асинхронный режим работы на двойной скорости

[lаtex] \begin{eqnarray} BAUD = \frac{F_{OSC}}{8(UBRRn+1)} \\ UBRR = \frac{F_{OSC}}{8BAUD}-1 \end{eqnarray}
[/lаtex]

Синхронный режим мастера

[lаtex] \begin{eqnarray} BAUD = \frac{F_{OSC}}{2(UBRRn+1)} \\ UBRR = \frac{F_{OSC}}{2BAUD}-1 \end{eqnarray}
[/lаtex]

По этому мало информации, вот из русскоязычного. Спасибо чуваку(прикрути комменты если прочтешь).

Скопировал от туда.

… При включении синхронного режима работы USART (это делается установкой бита UMSEL в регистре UCSRC в единичное состояние) вместо внутреннего источника частоты для тактирования сдвигового регистра используется третий вывод порта под названием ХСК (по каким-то причинам этот вывод в большинстве моделей совпадает с выводом ТО — для подключения внешнего источника импульсов Timer0). При этом генератор тактирующих импульсов USART попросту переключается на этот контакт, если он установлен на выход (режим ведущего), либо, если он установлен на вход, вообще отключается, и USART тактируется внешними импульсами на этом выводе (режим ведомого).

Синхронный режим работы USART недаром изложен в руководствах достаточно «мутно» — мне неизвестны разработки этой возможности. Очевидно, что в синхронном режиме гораздо удобнее задействовать интерфейс SPI, который не имеет «заморочек» со стартовыми-стоповыми битами (они необходимы только при асинхронном обмене, а при синхронном лишь увеличивают время передачи) и к тому же работает значительно быстрее.

Одно из известных мне нестандартных применений синхронного режима — вывод ХСК может служить аппаратным генератором прямоугольных импульсов, если установить его на выход, a USART в режим непрерывного синхронного приема (для этого достаточно разрешить синхронный режим установкой бита UMSEL и прием установкой бита RXEN, а на выводе RxD навсегда установить логический ноль, соответствующий старт-биту).

Некоторый интерес может вызвать другая функция расширенного порта — возможность работы в режиме мультипроцессорного обмена, которая удобна, например, для организации взаимодействия устройств по интерфейсу RS-485 (см. далее). Вкратце он заключается в следующем. Несколько контроллеров соединяются по выводам RxD и TxD параллельно. Один из них назначается ведущим, и у него устанавливается 9-битовый режим обмена. Все остальные (ведомые) контроллеры переключаются в режим мультипроцессорного обмена установкой бита MPCM в регистре UCSRA. В этом состоянии они ожидают приема адресного байта от ведущего, отличающегося тем, что сопровождающий его девятый бит должен быть установлен в 1. Сам адрес при этом располагается в остальных восьми битах и устанавливается по договоренности программистом. Если ведомый контроллер распознал свой адрес, то он сбрасывает бит MPCM, переходя, таким образом, в обычный режим (все это делается программно!), после чего осуществляется обмен между ним и ведущим. Девятый бит при этом должен быть в нулевом состоянии, и остальные МК будут его игнорировать. Следует напомнить, что данные в UART всегда передаются младшими битами вперед, и если в адресуемом МК установлен 8-битовый режим приема, то при нулевом девятом бите будет регистрироваться ошибка кадра (бит FE). …

Продолжаем исследования.

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 и мы считываем наш байт

Теоретически эта программа должна заполнить терминал одни символом А