- •Медицинские микропроцессорные системы
- •Анисимов а.А.
- •Isbn © сПбГэту «лэти», 2019 введение
- •1. Общая структура микроконтроллеров avr
- •2. Программирование микроконтроллеров на языке ассемблер
- •3. Работа с отладочной платой attiny104-xnano
- •4. Устройство портов ввода-вывода
- •5. Таймеры-счётчики
- •6. Широтно-импульсная модуляция
- •7. Использование аналого-цифрового преобразователя
- •8. Передача данных по uart
- •9. Последовательный интерфейс spi
- •Приложение 1. Основные команды языка assembler для микроконтроллера attiny104
- •Список литературы
- •Оглавление
- •Медицинские микропроцессорные системы
- •197376, С.-Петербург, ул. Проф. Попова, 5
8. Передача данных по uart
Каждый микроконтроллер семейства Mega и некоторые из семейства Tiny имеет в своем составе аппаратный блок универсального последовательного интерфейса – UART. По структуре это обычный асинхронный последовательный протокол, то есть передающая сторона по очереди выдает в линию нули и единицы, а принимающая отслеживает и запоминает их. Синхронизация идет по времени – мы заранее задаем скорость передачи данных как на стороне передатчика информации, так и на стороне приемника (именно поэтому передача асинхронная – отдельная линия для передачи тактовых синхронизирующих импульсов отсутствует). Это весьма важное замечание, так как расхождение в скоростях передачи может привести к ошибкам в распознавании информации, при этом ошибки будут накапливаться с увеличением скорости передачи данных.
Интерфейс UART – двухпроводной (не считая линии земли), линии имеют разное назначение – линия RXD используется для приема сообщений (Receiver), а TXD – для передачи сообщений (Transmitter). UART – полнодуплексный интерфейс. Это значит, что приемник и передатчик работают независимо друг от друга, позволяя одновременно как передавать, так и принимать данные (полнодуплексная передача данных).
Протокол передачи данных.
Передача (соответственно, и прием) сообщений осуществляется фиксированными пакетами битов (такой пакет называют кадром). Кадр состоит из стартового бита (с него начинается каждый кадр), битов данных (их количество можно изменять от 5 до 9), бита проверки четности (проверка правильности передачи данных) и одного или двух стоп-битов (обязательный сигнал окончания кадра).
Вначале передатчик переводит линию в уровень логического нуля - это стартовый бит. Всё время в промежутках между пакетами данных линия передатчика находится в состоянии логической единицы, поэтому зафиксировав появление ноля (тот самый стартовый бит), приемник отсчитывает интервал Т1 и считывает первый бит данных, потом через интервалы Т2 считывает все оставшиеся биты. Последний бит (или два бита) является стоповым. Он сообщает принимающей стороне, что передача данных закончена.
В конце байта, перед стоповым битом, может находиться бит проверки четности. Так называют контрольный бит, служащий для проверки общей чётности двоичного числа (чётности количества единичных битов в числе). Он формируется при выполнении операции «Исключающее-ИЛИ» со всеми передаваемыми битами. Стоповых битовых может быть два, для повышения надежности передачи. Все эти параметры задаются строго перед началом передачи данных и не могут быть изменены в процессе. Самый распространенный формат передачи данных для микроконтроллеров – 8 бит данных, 1 стартовый бит, 1 стоповый бит, без проверки на четность.
Рис. 8.1. Пакет данных для передачи по интерфейсу UART
Настройка и программирование интерфейса UART.
Вначале рассмотрим регистры, имеющие отношение к нашему приёмопередатчику.
У каждого микроконтроллера, поддерживающего UART, есть регистр UDRx – UART Data Register (в микроконтроллере AtMega128 два UART’а, поэтому и регистров тоже два – UDR0 и UDR1). На физическом уровне UDR представляет собой два разных регистра, имеющих одинаковый адрес. При записи данных информация поступает в первый регистр (регистр передатчика), а при чтении берется из другого (регистр приемника). Таким образом, и при чтении, и при записи данных мы используем один регистр, что значительно облегчает процесс программирования.
О том, что байт полностью пришел в регистр UDR, нам указывает прерывание по завершению приема, которое вызывается сразу же после того, как приемник обработает все поступившие в него биты.
Поскольку передача идет довольно медленно, то бездумно посылать всё новые и новые данные в регистр UDR нельзя – необходимо дождаться окончания передачи предыдущего байта. О том, что UDR пуст и готов к приему нового байта сигнализирует бит UDRE, он же вызывает аппаратное прерывание по опустошению буфера (USART Data Register Empty Interrupt Enable).
Все настройки приемопередатчика хранятся в регистрах конфигурации, которые известны как UCSRA, UCSRB и UCSRC, скорость передачи данных задается регистровой парой UBBRH:UBBRL. Рассмотрим подробнее наиболее важные регистры.
Регистр UCSRxA (UART Control and Status Register).
Здесь наибольший интерес вызывают биты RXCn (Receive Complete) и TXCn (Transmit Complete) – это флаги завершения приема и передачи данных, соответственно. RXC устанавливается в единицу, когда принимаемый байт запишется в регистр UDR для последующего чтения, а TXC – после прохождения последнего стоп-бита и отсутствия новых данных в регистре UDR, то есть после окончания передачи всех битов.
Также одновременно с этими флагами вызывается прерывание (если оно было разрешено). Сбрасываются эти флаги на аппаратном уровне – принимающий после чтения данных из регистра UDR, передающий при переходе на соответствующее прерывание, либо программным образом (чтобы сбросить флаг программно, в него надо записать логическую единицу).
Биты UDREn (USART Data Register Empty) сигнализирует о том, что регистр UDR приемника пуст, и в него можно записывать новый байт. Сбрасывается он аппаратно после записи новой порции данных в UDR, при этом генерируется соответствующее прерывание.
Бит U2Xn (Double the USART Transmission Speed) – бит удвоения скорости передачи данных при работе в ассинхронном режиме. Его надо учитывать при расчете значения в регистровой паре UBBRH:UBBRL.
Регистр UCSRxB.
В этом регистре основную роль играют биты RXEN (Receiver Enable) и TXEN (Transmitter Enable) – при записи в них логической единицы осуществляется разрешение приема и передачи, соответственно.
RXCIEn (RX Complete Interrupt Enable) – осуществляет разрешение прерывания по завершению приема данных.
TXCIE (TX Complete Interrupt Enable) – осуществляет разрешение прерывания по завершению передачи данных.
UDRIEn (UART Data Register Empty Interrupt Enable) – осуществляет разрешение прерывания по опустошению регистра передачи данных UDR.
Регистр UCSRxC
В этом регистре мы можем выбрать необходимый протокол передачи данных.
UMSELn (UART Mode Select) – здесь мы задаем формат передачи – синхронный либо асинхронный, в зависимости от значения бита, как показано в таблице 8.1.
Таблица 8.1. Выбор режима передачи данных по UART
UMSELn (значение бита) |
Режим передачи |
0 |
Асинхронный |
1 |
Синхронный |
UPMn1:0 (UART Parity Mode) – установка проверки на четность, в наших практических работах данная опция не используется, по умолчанию в оба бита записаны логические нули, передача данных ведется без проверки на четность.
USBSn (UART Stop Bit Select) – определяет количество стоповых битов, здесь возможно всего два варианта, в соответствии с таблицей 8.2.
Таблица 8.2. Выбор количества стоп-бит.
USBSn Stop Bit (значение бита) |
Количество стоп-бит |
0 |
1 стоповый бит |
1 |
2 стоповых бита |
UCSZn1:0 (UART Character Size) – определяет количество передаваемых битов данных – от 5 до 9, в соответствии с таблицей 8.3.
Таблица 8.3. Выбор количества передаваемых бит данных.
UCSZn2 |
UCSZn1 |
UCSZn0 |
Количество бит |
0 |
0 |
0 |
5 бит |
0 |
0 |
1 |
6 бит |
0 |
1 |
0 |
7 бит |
0 |
1 |
1 |
8 бит |
1 |
0 |
0 |
зарезервировано |
1 |
0 |
1 |
зарезервировано |
1 |
1 |
0 |
зарезервировано |
1 |
1 |
1 |
9 бит |
Скорость передачи данных
Здесь всё зависит от значения, записанного в регистровую пару UBBRH:UBBRL. Вычисляется требуемое значение по следующей формуле:
для U2X=0
(обычная скорость передачи);
для
U2X=1
(удвоенная скорость передачи),
где XTAL – рабочая тактовая частота контроллера, baudrate – требуемая скорость. Чем выше скорость, тем больше ошибка рассинхронизации, поэтому на высоких скоростях целесообразно использовать специализированные кварцы (те самые, с неровным значением вроде 11.0592 МГц).
Теперь можно приступить к процедуре непосредственной инициализации, используется стандартный протокол передачи, и единственный канал UART’а, доступный для нашего микроконтроллера. Задача простейшей программы для передачи данных на персональный компьютер крайне проста – в бесконечном цикле мы увеличиваем значение регистра на единицу, начиная с нуля, передаём полученное значение, далее вызываем подпрограмму задержки, затем цикл повторяется.
Программа на языке Ассемблер.
//****************************************************************//
// вектор прерываний
ORG 0x000
RJMP RESET // Reset Handler
ORG 0x001
RETI // IRQ0 Handler
ORG 0x002
RETI //PCINT0 Handler
ORG 0x003
RETI // PCINT1 Handler
ORG 0x004
RETI // Timer/Counter0 Capture Handler
ORG 0x005
RJMP Tim_OVF // Timer/Counter0 Overflow Handler
ORG 0x006
RETI // Timer/Counter0 Compare Match A Handler
ORG 0x007
RETI // Timer/Counter0 Compare Match B Handler
ORG 0x008
RETI // Analog Comparator Handler
ORG 0x009
RETI // Watchdog Timer Handler
ORG 0x00A
RETI // Voltage Level Monitor Handler
ORG 0x00B
RETI // ADC Conversion Handler
ORG 0x00C
RETI // USART0 Rx Start Handler
ORG 0x00D
RETI // USART0 Rx Complete Handler
ORG 0x00E
RETI // USART0 Data Register Empty Handler
ORG 0x00F
RETI // USART0 Tx Complete Handler
ORG INT_VECTORS_SIZE // Конец таблицы прерываний
RESET:
// Обязательная инициализация стека
LDI R16, Low(RAMEND)
OUT SPL, R16
LDI R16, High(RAMEND)
OUT SPH, R16
//Инициализация периферии
.equ XTAL = 8000000 // Тактовая частота МК
.equ baudrate = 9600 // Скорость передачи данных
.equ bauddivider = (XTAL/(16*baudrate)-1) // Расчет коэффициента
LDI R16, 0xD8
OUT CCP, R16
LDI R16, 0x00
OUT CLKPSR,R16
// Инициализируем UART
LDI R16, bauddivider>>8
OUT UBRRH, R16
LDI R16, bauddivider
OUT UBRRL, R16
LDI R16, (1 << UCSZ0) | (1 << UCSZ1)
OUT UCSRC, R16
// Прерывания разрешены, передача данных разрешена
LDI R16, (1 << TXEN)
OUT UCSRB, R16
LDI R20, 0x00
//*******************************************************//
// Основной цикл программы
Main:
// Увеличиваем данные в регистре на единицу (инкремент)
INC R20
OUT UDR, R20 // Помещаем данные (R16) в буфер и отсылаем их
RCALL Delay // Вызываем подпрограмму задержки
RJMP Main // Возвращаемся в основной цикл
//****************************************************************//
// Обработка подпрограмм
.equ LowByte = 255
.equ MedByte = 255
.equ HighByte = 40
Delay:
LDI R16, LowByte // Грузим три байта
LDI R17, MedByte // Нашей задержки
LDI R18, HighByte
loop:
dec R16
brne loop
dec R17
brne loop
dec R18
brne loop
ret
Повторяем ту же самую программу на языке Си.
//****************************************************************//
#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE ((F_CPU / (USART_BAUDRATE * 16UL)) - 1)
int main(void)
{
CCP = 0xD8;
CLKPSR = 0x00;
UCSRC = (1 << UCSZ0) | (1 << UCSZ1);
UBRRH = (BAUD_PRESCALE >> 8);
UBRRL = BAUD_PRESCALE;
UCSRB = (1 << TXEN); // Enable Tx on USART
uint8_t counter = 0;
while (1)
{
UDR = counter;
while (!(UCSRA & (1 << UDRE))) {};
counter++;
_delay_ms(1000);
}
}
