Atmel 9 bit UART или подключение к MDB

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 и отправили на устройство.

Передача данных:

Sending Frames with 9 Data Bits
If 9-bit characters are used (UCSZ = 7), the ninth bit must be written to the TXB8 bit in UCSRB before the
Low byte of the character is written to UDR. The following code examples show a transmit function that
handles 9-bit characters. For the assembly code, the data to be sent is assumed to be stored in registers R17:R16


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;
}
 

Прием данных:

Receiving Frames with 9 Data Bits
If 9-bit characters are used (UCSZ=7) the ninth bit must be read from the RXB8 bit in UCSRB before
reading the low bits from the UDR. This rule applies to the FE, DOR and PE Status Flags as well. Read
status from UCSRA, then data from UDR. Reading the UDR I/O location will change the state of the receive buffer FIFO and consequently the TXB8, FE, DOR, and PE bits, which all are stored in the FIFO,
will change.
The following code example shows a simple USART receive function that handles both 9-bit characters
and the status bits.

 

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);
  }
 
}