9.4.9. Программный UART

(Руководство разработчика по микроконтроллерам семейства HCS08)

Одно из интересных применений модуля TPM в режиме сравнения — создание программного протокола передачи данных для асинхронного обмена. В достаточно развитых МК асинхронный обмен поддерживается средствами модуля SCI. Другое название этого модуля — универсальный асинхронный приемопередатчик (Universal Asynchronous Receiver-Transmitter — UART).

Программный UART — полезное приложение для МК без встроенного модуля последовательного асинхронного обмена (например, для МК серий QD и Rx) или когда необходим дополнительный модуль UART с невысокой скоростью обмена.

Программный модуль UART использует 2 канала модуля TPM: один — для передачи данных, а второй — для приема. В Примере 9.15 используется МК MC9S08QG8 на демонстрационной плате DEMO9S08QG8. Линия PTB1 устанавливается/сбрасывается при каждом событии выходного сравнения модуля TPM. Частота передачи данных устанавливается в регистре данных канала 0, сконфигурированного на генерацию прерываний без изменения состояния выхода. Канал генерирует прерывания, соответствующие интервалам передачи одного бита данных (104мкс) при скорости обмена 9600бит/с.

При каждом событии сравнения значение переменной tx_state уменьшается. Когда tx_state становится равным 0, генерируется старт-бит исходящего сигнала: TX_PIN устанавливается в 0. Одновременно в буфер передачи записывается значение, которое необходимо переслать.

Если tx_state находится в диапазоне от 0 до 9, то значение соответствующего бита передаваемого значения выставляется на выход TX_PIN, начиная с младшего бита передаваемого байта.

Если tx_state становится равным 9, то на линии TX_PIN генерируется стоп-бит. Если бит tx_state = 10, то операция передачи данных заканчивается: tx_state устанавливается в 0, и флаг tx_enable также сбрасывается в 0. Флаг tx_enable указывает, происходит ли процесс передачи данных (tx_enable = l) или нет (tx_enable = 0). Флаг устанавливается в функции передачи данных и автоматически сбрасывается после передачи каждого байта данных.

На Рис.9.13 показана временная диаграмма напряжения на линии и основные моменты работы модуля TPM при передаче кода 0x82.

Рис.9.13. Временная диаграмма сигнала на линии при передачи кода 0x82.

Приемник сигнала построен на основе канала 1 модуля TPM. Когда приемник не действует, то включено прерывание KBI для вывода PTB0. При обнаружении спадающего фронта сигнала на выводе PTB0, режим прерывания KBI для этого вывода программно отключается и назначается режим работы в качестве входа канала 1 модуля TPM, который работает в режиме выходного сравнения с генерацией запроса на прерывание по событию в канале. Первое значение для сравнения устанавливается равным половине периода текущего значения TPMCNT. Это позволяет совершить первое прерывание примерно посередине интервала старт-бита.

По первому прерыванию считывается сигнал на выводе RX и, если он равен 0, то это значит, что обнаружен корректный старт-бит. Далее по каждому событию прерывания увеличивается значение переменной rx_state, которая показывает текущее состояние передачи данных. Учтите, что после первого сравнения значение регистра данных канала 1 программно изменяется, так как после первого сравнения прерывания должны следовать с частотой поступления данных, или 104мкс для скорости обмена 9600бит/с.

Рис.9.14. Временная диаграмма.

Эта операция продолжается до тех пор, пока значение переменной rx_state не станет равным 9. Если в этот момент обнаруживается корректный стоп-бит (ВЫСОКИЙ уровень на линии RX), то работа канала 1 модуля TPM запрещается, он отключается от линии PTB0, для линии снова назначается функция KBI. Устройство ожидает следующего старт-бита.

На Рис.9.14 показана временная диаграмма напряжения на линии и основные моменты работы модуля TPM при приеме кода 0x82.

Пример 9.15. Программная реализация приемопередатчика UART

//ДемонстрационнаяплатаDEMO9S08QG8,программныйUART
//ЛинияTxD-PTB1
//ЛинияRxD-PTB0
//BUSCLK=4МГц
//Скоростьпередачи9600бит/с
//АвторFabioPereira-fabio@sctec.com.br
//www.sctec.com.br
#include/*forEnableInterruptsmacro*/
#include"derivative.h"/*includeperipheraldeclarations*/
#include"hcs08.h"/*Этонашфайлсобъявлениями!*/
#defineTX_PINPTBD_PTBD1//PTB1-линияTX
#defineRX_PINPTBD_PTBD0//PTB0-линияRX
#defineRX_KBIBIT_4//PTB0(RX)–4-ялиниямодуляKBI
#defineBAUDRATE9600//Скоростьпередачиданных
#defineTPMCK1000000//ТактированиеTPMCK
#defineBITTIMETPMCK/BAUDRATE//Времяпередачиодногобита
chartx_data,rx_data;
#pragmaDATA_SEG__DIRECT_SEGMY_ZEROPAGE
struct
{
chartx_enable:1;//Работапередатчика
charrx_flag:1;//Флагпринятогосимвола
}__nearflags;
//Директива«#pragmaDATA_SEG__DIRECT_SEGMY_ZEROPAGE»
//размещаетфлаги«tx_enable»и«rx_flag»внулевойстраницепамяти,
//чтобыможнобылобыстрообратитьсякомандамибитовогопроцессора.
#pragmaDATA_SEGDEFAULT
//Подпрограммапрерываниятаймерагенерирует
//тактовыеимпульсыдляпередатчика
voidinterruptVectorNumber_Vtpmch0tpmch0_isr(void)
{
staticchartx_state;
#pragmaDATA_SEG__DIRECT_SEGMY_ZEROPAGE
static__nearcharbuffer;
//Размещаемпеременную«__nearcharbuffer»
//внулевойстраницепамятидляболеебыстрогодоступа.

#pragmaDATA_SEGDEFAULT
TPMC0SC_CH0F=0;//Сбросфлагапрерывания
TPMC0V+=BITTIME;//Следующеесравнениечерез104мкс
if(flags.tx_enable)
{
if(!tx_state)//Старт-бит
{
TX_PIN=0;
buffer=tx_data;
tx_state++;
}
else
if(tx_state<9)//Ббитданных
{
TX_PIN=buffer&1;//Выводиммладшийбитданных
buffer>≥1;//Сдвигаембуферна1битвправо
tx_state++;
}
else
if(tx_state==9)//Стоп-бит
{
TX_PIN=1;
tx_state++;
}
else//Передачазавершена
{
tx_state=0;//tx_stateвначальноезначение
flags.tx_enable=0;//Отключаемпередатчик
}
}
}
//Подпрограммапрерываниятаймера,
//генерируеттактовыеимпульсыдляпринятияданных
voidinterruptVectorNumber_Vtpmch1tpmch1_isr(void)
{
staticcharrx_state;
#pragmaDATA_SEG__DIRECT_SEGMY_ZEROPAGE
static__nearcharbuffer;
#pragmaDATA_SEGDEFAULT
TPMC1SC_CH1F=0;//Сбросфлагапрерывания
TPMC1V+=BITTIME;//Следующеесравнениечерез104мкс
if(!rx_state)//Старт-бит?
{
if(!RX_PIN)
{
buffer=0;//Да,этостарт-бит
rx_state++;//Следующийбитбудетнулевымбитомданных
}
else
{
KBIPE=RX_KBI;//НазначитьфункциюKBIдлявыводаприемника
KBISC_KBACK=1;//Сбросфлагапрерывания
TPMC1SC=0;
}
}
elseif(rx_state<9)//Принятбитданных
{
buffer>≥1;//Сдвигбуферана1битвлево
if(RX_PIN)buffer|=128;
rx_state++;
}
else
{
if(RX_PIN)//Принятстоп-бит?
{
rx_data=buffer;
flags.rx_flag=1;//Установкафлагаприемника
}
KBIPE=RX_KBI;//СнованазначитьфункциюKBIдлявыводаприемника
KBISC_KBACK=1;//Сбросфлагапрерывания
rx_state=0;
TPMC1SC=0;
}
}
//Подпрограммаустановкиинтерваловдляпрерыванийпотаймерувовремяприема
voidinterruptVectorNumber_Vkeyboardkbi_isr(void)
{
KBISC_KBACK=1;//Сбросфлагапрерывания
TPMC1SC=bCHIE|TPM_COMPARE_INT;
TPMC1V=TPMCNT+BITTIME/2-1;
KBIPE_KBIPE4=0;//ОтключениегенерациипрерываниялиниейRX
}
//Подпрограммапередачистрокиданных
voidsend_serial_string(char*string)
{
while(*string)//Длявсехсимволовстроки
{
while(flags.tx_enable);//Ожидание,покапередатчикнеосвободится
tx_data=*string;//Записьтекущегосимволавбуферпередатчика
flags.tx_enable=1;
string++;//Переходкследующемусимволустроки
}
}
voidmcu_init(void)
{
SOPT1=bBKGDPE;//Включениевозможностифоновойотладки
ICSSC=NV_FTRIM;//НастройказначенияFTRIM
ICSTRM=NV_ICSTRM;//НастройказначенияTRIM
ICSC2=0;
//ЧастотаBUSCLKравна8МГц
//Настройкалинийввода/вывода
PTBDD=BIT_1;//НастройкаPTB1навывод
//НастройкамодуляTPMнагенерациюсчастотойпередачиодногобитаданных
TPMSC=TPM_BUSCLK|TPM_DIV8;//TPMCK=1МГц
TPMC0SC=bCHIE|TPM_COMPARE_INT;
//НастройкалинииRxнагенерациюпрерывания(модульKBI)
KBISC=bKBIE;
KBIPE=RX_KBI;//Бит4=PTB0
EnableInterrupts;
flags.tx_enable=0;
flags.rx_flag=0;
TX_PIN=1;//ЛинияTXсвободна
}
voidmain(void)
{
mcu_init();
send_serial_string("Thisisatest!/r/n");
while(1)
{
if(flags.rx_flag)
{
tx_data=rx_data;
flags.rx_flag=0;
flags.tx_enable=1;
}
}
}

Для изменения линий RX и TX необходимо изменить директиву #define в начале программы. Также должно быть изменено соответствие #define RX_KBI так, чтобы прерывание KBI происходило по сигналу, поступающему на вход RX.

Учтите, что описанные выше проблемы с временной задержкой обработки прерывания играют огромную роль при организации протокола UART, особенно для приемника. Для приемника, настроенного на скорость 9600бит/с, канал RX должен быть прецизионно настроен на период 104.17мкс.

Используя TPMCK с частотой 1МГц, счетчик TPM увеличивается каждую 1мкс. Для отсчета интервалов приема одного бита необходимо установить уровень сравнения, равный 104. При этом дробная часть уже теряется.

Если частота BUSCLK равна 8МГц, то максимальная задержка выполнения подпрограммы прерывания равна 22 тактам BUSCLK, или 2.75мкс. Сброс флага прерывания канала и изменение состояния регистра TPMC0V занимает 16 тактов BUSCLK, а запись в стек регистра H занимает еще 2 такта BUSCLK. Сохранение регистра H в стек добавляется автоматически компилятором, так как регистр H:X используется в подпрограмме прерывания. Общая задержка составляет 40 тактов BUSCLK, или 5мкс на частоте 8МГц. Для более подробной информации проанализируете приведенный ниже программный код.

voidinterruptVectorNumber_Vtpmch0tpmch0_isr(void)
{
staticchartx_state;
#pragmaDATA_SEG__DIRECT_SEGMY_ZEROPAGE
static__nearcharbuffer;
#pragmaDATA_SEGDEFAULTPSHH;2такта
TPMC0SC_CH0F=0;→BCLR7,_TPMlC0SC.Byte;5тактов
TPMC0V+=BITTIME;→┌LDHX_TPMC1V.Word;4такта
}│AIX#104;2такта
└STHX_TPMC1V.Word;5тактов
...

Задержка 5мкс играет большую роль, поскольку она составляет 4.8% от времени, в течении которого бит данных установлен на линии. Для приемника эта задержка играет еще большую роль, поскольку ошибка накапливается во время приема данных. Если она будет очень большой, то последние биты (6, 7 и стоп-бит) могут быть обработаны неверно. В таком случае, высокая частота BUSCLK и быстрая обработка прерывания крайне важны.

Учтите, что в данном случае запись значения в регистр TPMCOV занимает немного меньше времени, чем в предыдущем примере, поскольку значение — константа, что позволяет компилятору генерировать оптимизированный код!


Электронные компоненты Freescale >>>
Подробнее о компании Freescale >>>