Задача
Связать микроконтроллер с компьютером. Микроконтроллер должен принимать по USART`у символ, отображать его на LCD и отвечать компьютеру “Ok”.
Аппаратная часть
Микроконтроллер ATmega8535 имеет в своем составе модуль универсального синхронно/асинхронного приемопередатчика - USART. С его помощью между компьютером и микроконтроллером можно организовать обмен данными по последовательному каналу. Раньше в компьютерах для этих целей использовались COM порты, однако на современных машинах они уже большая редкость. Если на вашем компьютере все-таки есть такой порт, то для подключения микроконтроллера, понадобится преобразователь уровней ТТЛ – RS232. Его можно собрать на микросхеме
MAX232AEPE.
Если COM порта нет, будем подключаться к USB. Для облегчения работы с этим интерфейсом и для поддержки старых устройств, использовавших RS232, производители микросхем выпускают специальные USB-UART преобразователи. Один из вариантов подобного преобразователя представлен на схеме ниже (микросхема
FT232BM). При подключении его к компьютеру, система попросит драйвера. Их можно скачать на сайте производителя.
Схема для нашего примера (без переходника)
Программная часть
Для работы с USART`ом нам нам понадобятся 4 функции. Три пользовательские, которые мы сможем вызывать:
Функция инициализации
Функция отправки символа
Функция чтения приемного буфера
И одна для обработки прерывания USART`а:
Обработчик прерывания по завершению приема
Функция инициализации
Как и любой другой периферийный модуль, USART перед использованием нужно настроить. Для этого в микроконтроллере ATmega8535 есть 5 регистров – UBRRH, UBRRL, UCSRA, UCSRB, UCSRC.
//инициализация usart`avoid USART_Init(
void)
{
UBRRH = 0;
UBRRL = 51;
//скорость обмена 9600 бод //разр. прерыв при приеме, разр приема, разр передачи. UCSRB = (1<<RXCIE)|(1<<RXEN)|(1<<TXEN);
//обращаемся к регистру UCSRS, размер слова – 8 бит UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
}
UCSRB Принятый символ USART модуль сохраняет в регистре данных UDR. Оттуда мы его потом переписываем в буфер. Выполняется это в прерывании, а для этого оно должно быть разрешено. Устанавливаем бит – RXCIE – разрешение прерывания по завершению приема в единицу.
Биты TXCIE и UDRIE – разрешают прерывания по завершению передачи и прерывания при очистке регистра данных соответственно. Эти прерывания мы сейчас использовать не будем.
Модуль USART состоит из трех основных частей – блока тактирования, блока передатчика и блока приемника. Для разрешения работы приемника и передатчика нужно установить в единицу биты RXEN и TXEN соответственно.
Размер слова данных передаваемых/принимаемых модулем USART может варьироваться от 5 до 9 разрядов и определяется битами UCSZ2..UCSZ0. В регистре UCSRB находится только бит UCSZ2. Остальные биты находятся в регистре UCSRC. Мы будем использовать 8-ми разрядные слова, а значит эти три бита должны быть установлены так - 011.
Биты RXB8 и TXB8 – это 8-ой разряд принимаемых/передаваемых данных соответсвенно. Они используются, если размер слова данных – 9 бит.
//разр. прерывания при приеме, разр. приема, разр. передачиUCSRB = (1<<RXCIE)|(1<<RXEN)|(1<<TXEN);
UCSRC У микроконтроллера ATmega8535 регистр UCSRC размещен по тому же адресу что и регистр UBRRH (это тоже регистр модуля USART, вместе с регистром UBRRL он определяет скорость обмена), поэтому при обращении к ним нужно выполнять ряд дополнительных действий для выбора конкретного регистра. За это отвечает бит URSEL. Если он установлен в 1 обращение производится к UCSRC. Устанавливаем его.
UMSEL – определяет режим работы модуля USART – синхронный, асинхронный. У нас режим работы асинхронный, разряд должен быть сброшен.
UPM1, UPM0 – определяют режимы работы схем контроля и формирования четности. Используются для повышения надежности передачи данных. В нашем случае контроль четности выключен – биты должны быть сброшены.
USBS – количество стоп битов. Для нашего случай бит сброшен.
UCSZ1, UCSZ0 – формат посылок. О них мы уже говорили выше. Для 8-ми разрядного слова эти биты должны быть установлены в единицы.
UCPOL – полярность тактового сигнала. Пропускаем мимо, потому что разряд используется только при работе в синхронном режиме.
//обращаемся к регистру UCSRS, размер слова – 8 битUCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
UBRR – UBRRH:UBRRL Скорость обмена данными определяется содержимым регистра UBRR. Это 12 разрядный регистр и он физически размещается в двух регистрах ввода/вывода - UBRRH:UBRRL. Скорость обмена выбирается из ряда стандартных значений, в нашем примере она равна 9600 бод. Значение UBRR для обычного асинхронного режима (есть еще асинхронный режим с удвоенной скоростью обмена) вычисляется по формуле:
UBRR = (Fck/(16*BAUD)) – 1
где Fck – тактовая частота микроконтроллера, BAUD скорость обмена в бодах
Для нашего случая UBRR = 8000000/(16*9600) – 1 = 51. Это округленное значение, поэтому реальная скорость обмена будет отличаться от 9600. Рекомендуется использовать такие значение регистра UBRR, при которых получаемая скорость передачи отличается от требуемой на величину < 0.5%. Большая ошибка будет снижать помехозащищенность линии передачи.
UBRRH = 0;
UBRRL = 51;
//скорость обмена 9600 бодUCSRA Этот регистр в основном содержит флаги устанавливаемые самим модулем USART. Единственный флаг который нам понадобится в программе – UDRE – флаг опустошения регистра данных. Он устанавливается в 1 при пустом буфере передатчика. Установленный флаг означает, что в регистр данных можно загружать новое значение.
Функция посылающая символ
//отправка символа по usart`у
void USART_SendChar(unsigned char sym)
{
while(!(UCSRA & (1<<UDRE)));
UDR = sym;
}
Передача данных USART`ом инициируется записью данных в буферный регистр передатчика – регистр данных UDR. (Работа передатчика естественно должна быть разрешена). Но перед тем как записать данные, нужно убедиться что передатчик освободился и готов к получению нового слова данных. Для этого в цикле while мы проверяем бит UDRE регистра UCSRA.
Обработчик прерывания по завершению приема
//прием символа по usart`у в буфер
#pragma vector=USART_RXC_vect
__interrupt void usart_rxc_my(void)
{
usartRxBuf = UDR;
}
Когда модуль USART принял данные, вызывается соответствующее прерывание. В прерывании мы переписываем содержимое регистра UDR в буфер usartRxBuf. Старое значение буфера при этом затрется, но так как мы используем медленный посимвольный обмен, потерь данных не будет
Функция чтения приемного буфера
//чтение буфера
unsigned char USART_GetChar(void)
{
unsigned char tmp = usartRxBuf;
usartRxBuf = 0;
return tmp;
}
Копируем значение буфера в локальную переменную, очищаем его. Локальную переменную возвращаем.
Необходимый минимум готов. Оформляем его в виде программного модуля.
usart.h
usart.c
Основная программа
Основная программа будет выглядеть так. Надеюсь дополнительных комментариев она не требует.
//*****************************************
// Author(s)...: Pashgan http://ChipEnable.Ru
// Target(s)...: ATMega8535
// Compiler....: IAR EWA 5.11A
// Description.: UART/USART. Получение первых результатов
//******************************************
#include <ioavr.h>
#include "lcd_lib.h"
#include "usart.h"
int main( void )
{
unsigned char sym;
USART_Init();
LCD_Init();
__enable_interrupt();
LCD_SendString("uart:");
while(1){
sym = USART_GetChar(); //читаем буфер
if (sym){ //если что-то приняли, то
LCD_Goto(6,0);
LCD_WriteData(sym); //отображаем на lcd принятый символ
USART_SendChar('O'); //отвечаем компу "Ok "
USART_SendChar('k');
USART_SendChar(' ');
}
}
return 0;
}
Для проверки результатов работы микроконтроллера потребуется программа - терминал. Вы можете скачать ее в разделе
полезный софт. Если лень собирать схему и возиться с реальным железом, можно проверить программу в Proteus`e.
Файлы
Comments
если поток данных между МК и ПК насыщен
( например идут данные разной структуры ,
при этом идет посылка определенных команд
) .
Или тут все зависит от мастерства программировани я .
На ПК я использую VB6 . Там почти все
делаю через обработчик событий CommEvent .
Может быть на ПК в этом случае есть более
фунциональный язык .
Со стороны МК использую CodeVision .
ATmega8.InputLen=1
ATmega8.RThreshold=1
Private Sub ATmega8_OnComm()
Select Case ATmega8.CommEvent
Case comEvReceive
chr = ATmega8.Input
'Здесь уже кому что надо
End Select
End Sub
При отправке на компе принимается группа из 6-7 символов. притом 5 из них одинаковые, а остальные какие-то кракозяблы. Ну никак это не похоже на ОК...
Вчем мой косяк?
- Кракозябы часто возникают, когда скорость обмена мк и компа не совпадают. У меня в схеме тактовая частота мк - 8МГц, скорость обмена - 9600.
У mega48 названия битов и регистров модуля USART отличаются от mega8535 и при перекомпиляции из нужно исправлять. Скинь мне проект на microsmaster'со бака'rambler.ru , я гляну его
Только мах232 почемуто на 3,3В не работает.Знаю что 5В надо, но другом проекте другой мах232 работал отлично.
Error[Pe020]: identifier "UCSZ1" is undefined
Error[Pe020]: identifier "UCSZ0" is undefined
Error[Pe020]: identifier "UDRE" is undefined
Разрешаем использование имен битов определенных в хидер файле
В General Options > System ставим галочку Enable bit definitions in I/O-Include files.
1. prog connector - это программатор?
2. txd и rxd - это uart/usart переходник?
Спасибо за ответы)
Заюзав ваш приклад використання USART
та знайшов помилку.... незнаю як в старих версіях WinAvr але в нових версіях оголошувати переривання потрібно
ISR(USART_TXC_v ect)а не ISR(USART_TX_vect)
ISR(USART_RXC_vect)а не ISR(USART_TX_ve ct)... до цього в мене просто 'завісав' МК Atmega8...
Таким образом, USART самостоятельно выгребет весь буфер и сам себя успокоит.
Идея любезно подсказана DI HALT'ом. (dihalt.ru)
а на практике такая потребность часто возникает?
Второе, пишу if (sym == 'q') {LED=1;)
if (sym == 'w') {LED=0;)
Вообще не работает, тыкаю на любую кнопку загарается LED и не гаснет
Подключать на прямую без МАХ232 но лучше FT232BM заменить на FT232RL. У RL обвязки никакой не надо там всего 2 конденсатора по питанию достаточно (а цена одинаковая).
К стати не соединяйте Экран USB разъемов с землей, FT232 к этому очень чувствительна и может начать сбоить(типа устройство не опознано) На ноутбуке этого не будет на на стационарном компе может получится.
Я очень долго с этим мучился.
К стати спасибо за доходчивое объяснения)))
Пишу диплом, и практически сразу стллкнулся с проблемой. Пишу в iar а отлаживаю в протеусе и авр-студии. Необходимо отправлять 3-и 2-ух байтных числа, как только отправляю байт (например все единицы), приходят одни нули. При проверке в студии обнаружил, что когда помещаю число в UDR, на следующем цикле UDR обнуляется. Прочитал что на самом деле для передачи используется другой регистр, а UDR на подобии буфера. Тогда попробовал присваивать UDR одно число и в следующей строчке другое, но UDR всё равно обнуляется, а не ждёт пока регистр отправки отправит предыдущий.
Думаю вопрос глупый, и что я банально что-то упустил из виду=)
у меня так работает - avr-libc 1:1.7.1-2
if (data=='A')
{
stat=1;
}
if (data=='B')
{
stat=0;
}
while (stat)
не получается(
Code:
sym = USART_GetChar(); //читаем буфер
if (sym){ //если что-то приняли, то
LCD_Goto(6,0);
LCD_WriteData(sym); //отображаем на lcd принятый символ
USART_SendChar('O'); //отвечаем компу "Ok "
USART_SendChar('k');
USART_SendChar(' ');
}
Если не получается, то может быть скорость неправильно настроена.
в том то и дело, что мне надо проверить не факт приема чего-то, а на конкретный символ.
Code:
if (sym == 'A'){
...
}
UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE);
UCSRC = (1 << URSEL) | (3 << UCSZ0);
возможно верно?
кристалл mega8515, скорость 9600, частота 8000000
Code:
UBRRH=0;
UBRRL=51; //скорость обмена 9600 бод
UCSRB=(1<<RXCIE)|(1<<RXEN)|(1<<TXEN); //разр. прерыв при приеме, разр приема, разр передачи.
UCSRC=(1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); //размер слова 8 разрядов
}
1843200 hz, 115200 бод, плучилось 0
светодиоды на переходнике горят оба - символ отправлен и что-то принято, но в одном терминале (keterm) отобраюжаются буквы, в другом SimpleTerm - вопросы, в третьем Advanced Serial Port Monitor - те же буквы. В терминалах выбирал 115200 бод.
UBRRH=0;
UBRRL=0; //скорость обмена 115200 бод
UCSRB=(1<<RXCIE)|(1<<RXEN)|(1<<TXEN); //разр. прерыв при приеме, разр приема, разр передачи.
UCSRC=(1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); //размер слова 8 разрядов
Просто всё - разрешаете прерывание по приёму байта, в прерывании пишите данные в кольцевой буфер, а в основной программе обрабатываете принятые данные.
если у кого есть такой кусочек кода для приема, буду благодарен)
Например так:
Code:
#define RX_BUFFER_SIZE 100
volatile uint8_t RxBuffer[RX_BUFFER_SIZE];
volatile uint8_t RxNext, RxFirst;
ISR(USARTC0_RXC_vect)
{
uint8_t rx_next=RxNext;
RxBuffer[rx_next]=USARTC0.DATA;
if (++rx_next>=RX_BUFFER_SIZE)
rx_next=0;
RxNext=rx_next;
}
void UsartReceive()
{
uint8_t rx_first=RxFirst, rx_next=RxNext;
while (rx_first!=rx_next) {
uint8_t new_byte=RxBuffer[rx_first];
// обработка данных
//
if (++rx_first>=RX_BUFFER_SIZE)
rx_first=0;
}
RxFirst=rx_first;
}
С примером проще разбираться))
предпологаю для защиты, например по уарту "плюётся" еденичка, а на втором конце по случайности "мёртвая" земля
мне кажется tx подтягивает к 5-ти вольтам, а rx всегда может только слушать линию(то есть всегда в высокоимпедансн ом состоянии)?
Code:
#include "usart.h"
//однобайтный буфер
volatile unsigned char usartRxBuf = 0;
unsigned char fl = 0;
struct my_strct {unsigned char buf; unsigned char flg;};
#define RXCIE 7
#define RXEN 4
#define TXEN 3
#define URSEL 7
#define UCSZ1 2
#define UCSZ0 1
//инициализация usart`a
void USART_Init(void)
{
UBRRH = 0;
UBRRL = 71; //скорость обмена 9600 бод для 11,0592МГц в даташите
UCSRB = (1<<RXCIE)|(1<<RXEN)|(1<<TXEN); //разр. прерыв при приеме, разр приема, разр передачи.
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); //размер слова 8 разрядов
}
#define UDRE 5
//отправка символа по usart`у
void USART_SendChar(unsigned char sym)
{
while(!(UCSRA & (1<<UDRE)));
UDR = sym;
}
//чтение буфера
my_strct USART_GetChar(void)// на это место компилятор ругается '(' expected
{
my_strct my;
//unsigned char tmp;
unsigned char saveState = SREG;
#asm("cli");
my.buf=usartRxBuf;
my.flg=fl;
//tmp = usartRxBuf;
usartRxBuf = 0;
fl=0;
SREG = saveState;
return my;
//return tmp;
}
//прием символа по usart`у в буфер
interrupt [USART_RXC] void usart_rxc_my(void)
{
usartRxBuf = UDR;
fl=1;
}
RSS feed for comments to this post