Перейдем к модификации описанного программного UART`a под конкретный камушек. Поскольку пишу я в основном про 8-ми разрядные AVR, то эти контроллеры и будем рассматривать.
Во всех микроконтроллерах семейства mega есть аппаратный модуль UART, а вот в микроконтроллерах семейства tiny нет. По идее программный UART может потребоваться именно для младшего семейства, но не исключаю возможность использования этого кода и в mega`ах. Пути эмбеддера неисповедимы.
Проекты написаны в двух вариантах – для ATTiny45 и для ATmega16. Впрочем, отличия там очень незначительные и касаются только инициализации и прерывания задействованного таймера.
Программный UART для Tiny45. Заголовочный файл
Первое, что я сделал - это создал заголовочный файл softUart.h. В него я прописал включение заголовочного файла ioavr.h, макроопределения и прототипы пользовательских функций. Прототипы, следуя своему стилю, обозвал с использованием приставки. В данном случае - “SUART_”. Это позволяет легко определять, к каким файлам относятся те или иные функции.
#ifndef SOFT_UART_H
#define SOFT_UART_H
#include <ioavr.h>
//___________________________Настройки_______________________
#ifndef F_CPU
#define F_CPU 8000000L //тактовая частота мк
#endif
#define PRESCALER 64L //предделитель таймера 1L,8L,64L,256L,1024L
#define BAUD_RATE 4800L //скорость обмена
#define IN_BUF_SIZE 32 //вместимость приемного буфера
//пин приемника
#define RX_PINX PINB
#define RX_PORTX PORTB
#define RX_DDRX DDRB
#define RX_PIN 1
//пин передатчика
#define TX_PORTX PORTB
#define TX_DDRX DDRB
#define TX_PIN 0
//_________________Пользовательские функции____________________
void SUART_Init(void);
void SUART_PutChar(char ch);
void SUART_PutChar(char ch);
char SUART_GetChar(void);
void SUART_FlushInBuf(void);
char SUART_Kbhit(void);
void SUART_TurnRxOn(void);
void SUART_TurnRxOff(void);
//____________________________________________________________
#endif //SOFT_UART_H
Макроопределения F_CPU, PRESCALER и BAUD_RATE используются для расчета таймерной константы, обеспечивающей заданную скорость обмена по UART`у. Константа PRESCALER может принимать только значения, записанные в комментарии - 1L,8L,64L,256L,1024L. При задании других значений, компилятор будет выдавать ошибку.
Также в заголовочный файл я добавил определения, задающие приемный и передающий вывод микроконтроллера.
Программный UART для Tiny45. Сишный файл
В сишный файл, как и требовалось, я добавил недостающие кусочки кода.
Макросы, используемые для обращения к приемному и передающему выводу.
#define get_rx_pin_status() RX_PINX & (1<<RX_PIN)
#define set_tx_pin_high() TX_PORTX |= (1<<TX_PIN)
#define set_tx_pin_low() TX_PORTX &= ~(1<<TX_PIN)
Функцию idle(), которую сделал пустой, поскольку она мне не требовалась.
void idle()
{
}
Функции void timer_set( int BAUD_RATE ) и void set_timer_interrupt( timer_isr ) удалил, а функцию инициализации программного UART`a, из которой они вызывались, подправил следующим образом.
//формула для расчета таймерной константы
#define TIMER_CONST (0xff - (F_CPU/(BAUD_RATE*3*PRESCALER)))
#if PRESCALER==1L
#define CSXX (0<<CS02)|(0<<CS01)|(1<<CS00)
#elif PRESCALER==8L
#define CSXX (0<<CS02)|(1<<CS01)|(0<<CS00)
#elif PRESCALER==64L
#define CSXX (0<<CS02)|(1<<CS01)|(1<<CS00)
#elif PRESCALER==256L
#define CSXX (1<<CS02)|(0<<CS01)|(0<<CS00)
#elif PRESCALER==1024L
#define CSXX (1<<CS02)|(1<<CS01)|(1<<CS00)
#else
#error "prescaller not correct"
#endif
void SUART_Init(void)
{
//инициализация флагов UART`a
flag_tx_ready = FALSE;
flag_rx_ready = FALSE;
flag_rx_waiting_for_stop_bit = FALSE;
flag_rx_off = FALSE;
rx_num_of_bits = 10;
tx_num_of_bits = 10;
//инициализация выводов мк
RX_DDRX &= ~(1<<RX_PIN);
RX_PORTX |= (1<<RX_PIN);
TX_DDRX |= (1<<TX_PIN);
TX_PORTX |= (1<<TX_PIN);
//инициализация таймера
TCCR0B = CSXX;
TCNT0 = TIMER_CONST;
TIMSK |= (1<<TOIE0);
}
Как видите, я задействовал 8-ми разрядный таймер Т0 в режиме NORMAL с прерыванием по событию «переполнение». Предделитель таймера (CSXX) определяется константой PRESCALER, а таймерная константа (TIMER_CONST) вычисляется с помощью макроса.
Основной код программного UART`a поместил в обработчик прерывания таймера Т0, добавив в его начало код перезаписи значения счетного регистра TCNT0.
#pragma vector = TIM0_OVF_vect
__interrupt void TimerOvf(void)
{
…..
TCNT0 = TIMER_CONST;
….
//основной код
….
}
И последняя моя доработка кода программного UART`a – добавление квалификатора volatile к переменным, который используются и в прерывании и в обычных функциях.
static volatile unsigned char inbuf[IN_BUF_SIZE];
static volatile unsigned char qin = 0;
static volatile unsigned char qout = 0;
static char flag_rx_waiting_for_stop_bit;
static char flag_rx_off;
static char rx_mask;
static volatile char flag_rx_ready;
static volatile char flag_tx_ready;
static char timer_rx_ctr;
static volatile char timer_tx_ctr;
static char bits_left_in_rx;
static volatile char bits_left_in_tx;
static char rx_num_of_bits;
static char tx_num_of_bits;
static char internal_rx_buffer;
static volatile char internal_tx_buffer;
static char user_tx_buffer;
Далее я сделал проект в IAR`е, подключил к нему полученную «либу» программного UART`а и написал пару строк кода для проверки результатов.
Думаю, теперь вы без проблем модифицируете этот софтовый UART под любой другой камень.


[code] [/code]