STM8L-Универсальный асинхронный приёмопередатчик (UART)
К сожалению обещанный второй компаратор я решил пропустить, да и с индикатором от Nokia 1100 вышла неувязка, решил передавать данные с АЦП в компьютер, поэтому сегодня кратенько об универсальном приёмопередатчике (USART).
Модуль универсального приемопередатчика (USART)
Возможности модуля:
- двухпроводный полно-дуплексный асинхронный обмен
- однопроводной полудуплексный обмен
- IrDA кодер-декодер
- максимальная скорость обмена SYSCLK/16
- два вектора прерываний (прием, передача), восемь источников прерываний
- мульти-процессорное взаимодействие (возможность адресации)
- и как обычно ещё много полезного :) …
Функциональная схема модуля:
Я думаю начать нужно с самого простого. Попробуем сконфигурировать модуль на передачу данных в ПК и передать пару байт.
Разрешаем тактирование модуля:
PCKENR1 |= CLK_PCKENR1_USART1;
Вы не поверите, но для “стандартного” режима обмена (восемь бит данных, один стоп бит и т.д.) достаточно разрешить работу передатчика и задать скорость обмена:
USART1->CR1 = 0;
USART1->CR3 = 0;
USART1->CR4 = 0;
USART1->CR5 = 0;
USART1->CR2 = USART_CR2_TEN;
Скорость обмена задается очень просто, нужно знать лишь частоту тактирования:
пример из документации:
Примечание: не понял, за чем такие хитрые перестановки
В моем случае тактовая частота равна 2 МГц, а желаемая скорость обмена 38400 бод, следовательно:
2000000 / 38400 = 52,083(3)
полученное значение больше 16, следовательно данная скорость обмена реализуема, округлим до целого, при этом погрешность составит 0,16% без учета погрешности тактового RC-генератора:
52d = 0034h
USART1->BRR2 = 0x04;
USART1->BRR1 = 0x03;
или можно “автоматизировать” процесс:
Ну вот теперь можно “инициализировать” и передать байт:
USART1->DR = 0xAA;
while(!(USART1->SR & USART_SR_TC))
{
}
Прием организуется аналогично. Однако использовать данный метод передачи не всегда удобно, иногда нужно передавать и принимать без “ожидания” для этого можно воспользоваться прерываниями.
Не хочу вдаваться в подробности реализации алгоритма FIFO буфера, поэтому просто приведу код.
Передача:
void mcu_usart_fifo_transmit(uint8_t data)
{
uint8_t tx_head;
tx_head = (uint8_t) ((usart_fifo.tx_buffer_head + 1)
& (uint8_t) (USART_TX_BUFFER_SIZE - 1));
while (tx_head == usart_fifo.tx_buffer_tail)
{
}
usart_fifo.tx_buffer[tx_head] = data;
usart_fifo.tx_buffer_head = tx_head;
USART1->CR2 |= USART_CR2_TIEN;
}
обработчик прерывания:
@far @interrupt void handler_usart1_tx(void)
{
uint8_t status;
uint8_t tx_tail;
uint8_t tx_head;
status = USART1->SR;
if (status & USART_SR_TC)
{
USART1->SR &= (uint8_t) (~USART_SR_TC);
}
if (status & USART_SR_TXE)
{
tx_head = usart_fifo.tx_buffer_head;
tx_tail = usart_fifo.tx_buffer_tail;
if (tx_head != tx_tail++)
{
tx_tail &= USART_TX_BUFFER_SIZE - 1;
usart_fifo.tx_buffer_tail = tx_tail;
USART1->DR = usart_fifo.tx_buffer[tx_tail];
}
else
{
USART1->CR2 &= (uint8_t) (~USART_CR2_TIEN);
}
}
}
Прием:
uint8_t mcu_usart_fifo_receive(uint8_t * data)
{
uint8_t rx_head;
uint8_t rx_tail;
rx_head = usart_fifo.rx_buffer_head;
if (rx_head == usart_fifo.rx_buffer_tail)
return USART_FIFO_NO_DATA;
rx_tail = (uint8_t) ((usart_fifo.rx_buffer_tail + 1)
& (uint8_t) (USART_RX_BUFFER_SIZE - 1));
usart_fifo.rx_buffer_tail = rx_tail;
*data = usart_fifo.rx_buffer[rx_tail];
return usart_fifo.rx_last_error;
}
обработчик прерывания:
@far @interrupt void handler_usart1_rx(void)
{
uint8_t status;
uint8_t rx_data;
uint8_t rx_last_error;
uint8_t rx_head;
status = USART1->SR;
rx_data = USART1->DR;
rx_last_error = 0;
if (status & USART_SR_FE)
rx_last_error |= USART_FIFO_ERROR_FRAME;
if (status & USART_SR_OR)
rx_last_error |= USART_FIFO_ERROR_OVERRUN;
if (status & USART_SR_NF)
rx_last_error |= USART_FIFO_ERROR_NOISE;
rx_head = (uint8_t) ((usart_fifo.rx_buffer_head + 1)
& (uint8_t) (USART_RX_BUFFER_SIZE - 1));
if (rx_head == usart_fifo.rx_buffer_tail)
{
rx_last_error |= USART_FIFO_ERROR_BUFFER_OVERFLOW;
}
else
{
usart_fifo.rx_buffer_head = rx_head;
usart_fifo.rx_buffer[rx_head] = rx_data;
}
usart_fifo.rx_last_error = rx_last_error;
}
Размер буферов для приема и передачи должен быть кратен двум, но при желании его можно поменять с соответствующим изменением кода.
Пример
С передачей достаточно просто, а с приемом немного сложнее, нужно проверять были ли приняты данные:
Если нужно передавать или принимать информацию блоками, то удобнее использовать ПДП (DMA). Возможно об этом в следующий раз.
comments powered by Disqus