Atmel 9 bit UART или подключение к MDB
В данной небольшой статье речь пойдет о 9-битном формате UART для микроконтроллера AVR atmega. Довольно редкая вещь и вот наконец встретилось применение 9-ббитному формату.
Недавно была задача по разработке собственного варианта купюроприемника с возможностью возврата денег. Для этого было необходимо подключиться по протоколу MDB (multi drop bus), в котором я к своему удивлению обнаружил 9-битный протокол. 9ый бит используется как mode bit и служит для определения направления передачи в общей шине (от master к slave или обратно).
Напрямую считывать такие данные затруднительно (хотя обычным 9600-8-N1 мы даже что-то считали), можно было бы использовать биты паритета или два стоповых, но это все равно заплатка вместо нужного 9-битного режима.
Для того, чтобы имитировать купюроприемник мы решили использовать плату на основе AVR с несколькими уартами, под рукой оказалась arduino Mega, в которой один уарт мы сделали стандартным 9600-8N1 и пустили в usb, а другой сделали 9-битным, "подточив" ардуину под свои цели (9-битного варианта для Serail.Begin не существует в стандартной библиотеке, да и экзотика это все-таки).
Смысл программы довольно простой: если пришли данные из 9-битного порта, то обрезали последний и отправили на usb-порт, если пришли данные с usb-порта, то добавили бит mode и отправили на устройство.
Передача данных:
void USART_Transmit (unsigned int data)
{
// Wait for empty transmit buffer
// Copy 9th bit to TXB8
while ( !( UCSR2A & (1<
UCSR2B &= ~(1<
// Put data into buffer, sends the data
if ( data & 0x0100 )
UCSR2B |= (1<
UDR2 = data;
}
Прием данных:
unsigned char USART_Receive (unsigned char *data)
{
// Wait for data to be received
if ( !(UCSR2A & (1< return 1;
unsigned char stat, resh, resl;
// Get status and 9th bit, then data from buffer
stat = UCSR2A;
resh = UCSR2B;
resl = UDR2;
// If error, return -1
if ( stat & ((1< return -1;
// Filter the 9th bit, then return
resh = (resh > > 1) & 0x01;
unsigned int res = ((resh < < 8) | resl); // 9 bit data
(*data) = (unsigned char)(res > > 1); // return only 8 hi bits
return 0;
}
Инициализация:
void mdb_uart_init()
{
UCSR2B = (1< <RXEN2) | (1< <TXEN2);
UBRR2H = 0;
UBRR2L = 103; // 9600bps @ 16 MHz
UCSR2C |= (1< <UCSZ21) | (1< <UCSZ20); // 9bit mode
UCSR2B |= (1< <UCSZ22); // 9bit mode
}
Основной код:
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
mdb_uart_init();
pinMode(13, OUTPUT); // blink led init
digitalWrite(13, LOW); // blink led
}
void loop() {
// put your main code here, to run repeatedly:
unsigned char in_t = 0;
if (!USART_Receive(&in_t))
Serial.write(in_t);
if (Serial.available() > 0)
{
if (digitalRead(13) == HIGH)
digitalWrite(13, LOW); // blink led
else
digitalWrite(13, HIGH); // blink led
int out_t = Serial.read();
// Serial.write(out_t);
unsigned int out_m = (unsigned int)(out_t < < 1) | 0x1;
USART_Transmit(out_m);
}
}