qmk

QMK Firmware
git clone git://git.z3bra.org/qmk.git
Log | Files | Refs | Submodules | LICENSE

serial.c (16660B)


      1 /*
      2  * WARNING: be careful changing this code, it is very timing dependent
      3  *
      4  * 2018-10-28 checked
      5  *  avr-gcc 4.9.2
      6  *  avr-gcc 5.4.0
      7  *  avr-gcc 7.3.0
      8  */
      9 
     10 #ifndef F_CPU
     11 #define F_CPU 16000000
     12 #endif
     13 
     14 #include <avr/io.h>
     15 #include <avr/interrupt.h>
     16 #include <util/delay.h>
     17 #include <stddef.h>
     18 #include <stdbool.h>
     19 #include "serial.h"
     20 //#include <pro_micro.h>
     21 
     22 #ifdef SOFT_SERIAL_PIN
     23 
     24 #ifdef __AVR_ATmega32U4__
     25   // if using ATmega32U4 I2C, can not use PD0 and PD1 in soft serial.
     26   #ifdef USE_I2C
     27     #if SOFT_SERIAL_PIN == D0 || SOFT_SERIAL_PIN == D1
     28       #error Using ATmega32U4 I2C, so can not use PD0, PD1
     29     #endif
     30   #endif
     31 
     32   #if SOFT_SERIAL_PIN >= D0 && SOFT_SERIAL_PIN <= D3
     33     #define SERIAL_PIN_DDR   DDRD
     34     #define SERIAL_PIN_PORT  PORTD
     35     #define SERIAL_PIN_INPUT PIND
     36     #if SOFT_SERIAL_PIN == D0
     37       #define SERIAL_PIN_MASK _BV(PD0)
     38       #define EIMSK_BIT       _BV(INT0)
     39       #define EICRx_BIT       (~(_BV(ISC00) | _BV(ISC01)))
     40       #define SERIAL_PIN_INTERRUPT INT0_vect
     41     #elif  SOFT_SERIAL_PIN == D1
     42       #define SERIAL_PIN_MASK _BV(PD1)
     43       #define EIMSK_BIT       _BV(INT1)
     44       #define EICRx_BIT       (~(_BV(ISC10) | _BV(ISC11)))
     45       #define SERIAL_PIN_INTERRUPT INT1_vect
     46     #elif  SOFT_SERIAL_PIN == D2
     47       #define SERIAL_PIN_MASK _BV(PD2)
     48       #define EIMSK_BIT       _BV(INT2)
     49       #define EICRx_BIT       (~(_BV(ISC20) | _BV(ISC21)))
     50       #define SERIAL_PIN_INTERRUPT INT2_vect
     51     #elif  SOFT_SERIAL_PIN == D3
     52       #define SERIAL_PIN_MASK _BV(PD3)
     53       #define EIMSK_BIT       _BV(INT3)
     54       #define EICRx_BIT       (~(_BV(ISC30) | _BV(ISC31)))
     55       #define SERIAL_PIN_INTERRUPT INT3_vect
     56     #endif
     57   #elif  SOFT_SERIAL_PIN == E6
     58     #define SERIAL_PIN_DDR   DDRE
     59     #define SERIAL_PIN_PORT  PORTE
     60     #define SERIAL_PIN_INPUT PINE
     61     #define SERIAL_PIN_MASK  _BV(PE6)
     62     #define EIMSK_BIT        _BV(INT6)
     63     #define EICRx_BIT        (~(_BV(ISC60) | _BV(ISC61)))
     64     #define SERIAL_PIN_INTERRUPT INT6_vect
     65   #else
     66   #error invalid SOFT_SERIAL_PIN value
     67   #endif
     68 
     69 #else
     70  #error serial.c now support ATmega32U4 only
     71 #endif
     72 
     73 //////////////// for backward compatibility ////////////////////////////////
     74 #ifndef SERIAL_USE_MULTI_TRANSACTION
     75 /* --- USE Simple API (OLD API, compatible with let's split serial.c) */
     76   #if SERIAL_SLAVE_BUFFER_LENGTH > 0
     77   uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0};
     78   #endif
     79   #if SERIAL_MASTER_BUFFER_LENGTH > 0
     80   uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0};
     81   #endif
     82   uint8_t volatile status0 = 0;
     83 
     84 SSTD_t transactions[] = {
     85     { (uint8_t *)&status0,
     86   #if SERIAL_MASTER_BUFFER_LENGTH > 0
     87       sizeof(serial_master_buffer), (uint8_t *)serial_master_buffer,
     88   #else
     89       0, (uint8_t *)NULL,
     90   #endif
     91   #if SERIAL_SLAVE_BUFFER_LENGTH > 0
     92       sizeof(serial_slave_buffer), (uint8_t *)serial_slave_buffer
     93   #else
     94       0, (uint8_t *)NULL,
     95   #endif
     96   }
     97 };
     98 
     99 void serial_master_init(void)
    100 { soft_serial_initiator_init(transactions, TID_LIMIT(transactions)); }
    101 
    102 void serial_slave_init(void)
    103 { soft_serial_target_init(transactions, TID_LIMIT(transactions)); }
    104 
    105 // 0 => no error
    106 // 1 => slave did not respond
    107 // 2 => checksum error
    108 int serial_update_buffers()
    109 {
    110     int result;
    111     result = soft_serial_transaction();
    112     return result;
    113 }
    114 
    115 #endif // end of Simple API (OLD API, compatible with let's split serial.c)
    116 ////////////////////////////////////////////////////////////////////////////
    117 
    118 #define ALWAYS_INLINE __attribute__((always_inline))
    119 #define NO_INLINE __attribute__((noinline))
    120 #define _delay_sub_us(x)    __builtin_avr_delay_cycles(x)
    121 
    122 // parity check
    123 #define ODD_PARITY 1
    124 #define EVEN_PARITY 0
    125 #define PARITY EVEN_PARITY
    126 
    127 #ifdef SERIAL_DELAY
    128   // custom setup in config.h
    129   // #define TID_SEND_ADJUST 2
    130   // #define SERIAL_DELAY 6             // micro sec
    131   // #define READ_WRITE_START_ADJUST 30 // cycles
    132   // #define READ_WRITE_WIDTH_ADJUST 8 // cycles
    133 #else
    134 // ============ Standard setups ============
    135 
    136 #ifndef SELECT_SOFT_SERIAL_SPEED
    137 #define SELECT_SOFT_SERIAL_SPEED 1
    138 //  0: about 189kbps
    139 //  1: about 137kbps (default)
    140 //  2: about 75kbps
    141 //  3: about 39kbps
    142 //  4: about 26kbps
    143 //  5: about 20kbps
    144 #endif
    145 
    146 #if __GNUC__ < 6
    147   #define TID_SEND_ADJUST 14
    148 #else
    149   #define TID_SEND_ADJUST 2
    150 #endif
    151 
    152 #if SELECT_SOFT_SERIAL_SPEED == 0
    153   // Very High speed
    154   #define SERIAL_DELAY 4             // micro sec
    155   #if __GNUC__ < 6
    156     #define READ_WRITE_START_ADJUST 33 // cycles
    157     #define READ_WRITE_WIDTH_ADJUST 3 // cycles
    158   #else
    159     #define READ_WRITE_START_ADJUST 34 // cycles
    160     #define READ_WRITE_WIDTH_ADJUST 7 // cycles
    161   #endif
    162 #elif SELECT_SOFT_SERIAL_SPEED == 1
    163   // High speed
    164   #define SERIAL_DELAY 6             // micro sec
    165   #if __GNUC__ < 6
    166     #define READ_WRITE_START_ADJUST 30 // cycles
    167     #define READ_WRITE_WIDTH_ADJUST 3 // cycles
    168   #else
    169     #define READ_WRITE_START_ADJUST 33 // cycles
    170     #define READ_WRITE_WIDTH_ADJUST 7 // cycles
    171   #endif
    172 #elif SELECT_SOFT_SERIAL_SPEED == 2
    173   // Middle speed
    174   #define SERIAL_DELAY 12            // micro sec
    175   #define READ_WRITE_START_ADJUST 30 // cycles
    176   #if __GNUC__ < 6
    177     #define READ_WRITE_WIDTH_ADJUST 3 // cycles
    178   #else
    179     #define READ_WRITE_WIDTH_ADJUST 7 // cycles
    180   #endif
    181 #elif SELECT_SOFT_SERIAL_SPEED == 3
    182   // Low speed
    183   #define SERIAL_DELAY 24            // micro sec
    184   #define READ_WRITE_START_ADJUST 30 // cycles
    185   #if __GNUC__ < 6
    186     #define READ_WRITE_WIDTH_ADJUST 3 // cycles
    187   #else
    188     #define READ_WRITE_WIDTH_ADJUST 7 // cycles
    189   #endif
    190 #elif SELECT_SOFT_SERIAL_SPEED == 4
    191   // Very Low speed
    192   #define SERIAL_DELAY 36            // micro sec
    193   #define READ_WRITE_START_ADJUST 30 // cycles
    194   #if __GNUC__ < 6
    195     #define READ_WRITE_WIDTH_ADJUST 3 // cycles
    196   #else
    197     #define READ_WRITE_WIDTH_ADJUST 7 // cycles
    198   #endif
    199 #elif SELECT_SOFT_SERIAL_SPEED == 5
    200   // Ultra Low speed
    201   #define SERIAL_DELAY 48            // micro sec
    202   #define READ_WRITE_START_ADJUST 30 // cycles
    203   #if __GNUC__ < 6
    204     #define READ_WRITE_WIDTH_ADJUST 3 // cycles
    205   #else
    206     #define READ_WRITE_WIDTH_ADJUST 7 // cycles
    207   #endif
    208 #else
    209 #error invalid SELECT_SOFT_SERIAL_SPEED value
    210 #endif /* SELECT_SOFT_SERIAL_SPEED */
    211 #endif /* SERIAL_DELAY */
    212 
    213 #define SERIAL_DELAY_HALF1 (SERIAL_DELAY/2)
    214 #define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY/2)
    215 
    216 #define SLAVE_INT_WIDTH_US 1
    217 #ifndef SERIAL_USE_MULTI_TRANSACTION
    218   #define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY
    219 #else
    220   #define SLAVE_INT_ACK_WIDTH_UNIT 2
    221   #define SLAVE_INT_ACK_WIDTH 4
    222 #endif
    223 
    224 static SSTD_t *Transaction_table = NULL;
    225 static uint8_t Transaction_table_size = 0;
    226 
    227 inline static void serial_delay(void) ALWAYS_INLINE;
    228 inline static
    229 void serial_delay(void) {
    230   _delay_us(SERIAL_DELAY);
    231 }
    232 
    233 inline static void serial_delay_half1(void) ALWAYS_INLINE;
    234 inline static
    235 void serial_delay_half1(void) {
    236   _delay_us(SERIAL_DELAY_HALF1);
    237 }
    238 
    239 inline static void serial_delay_half2(void) ALWAYS_INLINE;
    240 inline static
    241 void serial_delay_half2(void) {
    242   _delay_us(SERIAL_DELAY_HALF2);
    243 }
    244 
    245 inline static void serial_output(void) ALWAYS_INLINE;
    246 inline static
    247 void serial_output(void) {
    248   SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
    249 }
    250 
    251 // make the serial pin an input with pull-up resistor
    252 inline static void serial_input_with_pullup(void) ALWAYS_INLINE;
    253 inline static
    254 void serial_input_with_pullup(void) {
    255   SERIAL_PIN_DDR  &= ~SERIAL_PIN_MASK;
    256   SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
    257 }
    258 
    259 inline static uint8_t serial_read_pin(void) ALWAYS_INLINE;
    260 inline static
    261 uint8_t serial_read_pin(void) {
    262   return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK);
    263 }
    264 
    265 inline static void serial_low(void) ALWAYS_INLINE;
    266 inline static
    267 void serial_low(void) {
    268   SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;
    269 }
    270 
    271 inline static void serial_high(void) ALWAYS_INLINE;
    272 inline static
    273 void serial_high(void) {
    274   SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
    275 }
    276 
    277 void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size)
    278 {
    279     Transaction_table = sstd_table;
    280     Transaction_table_size = (uint8_t)sstd_table_size;
    281     serial_output();
    282     serial_high();
    283 }
    284 
    285 void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size)
    286 {
    287     Transaction_table = sstd_table;
    288     Transaction_table_size = (uint8_t)sstd_table_size;
    289     serial_input_with_pullup();
    290 
    291     // Enable INT0-INT3,INT6
    292     EIMSK |= EIMSK_BIT;
    293 #if SERIAL_PIN_MASK == _BV(PE6)
    294     // Trigger on falling edge of INT6
    295     EICRB &= EICRx_BIT;
    296 #else
    297     // Trigger on falling edge of INT0-INT3
    298     EICRA &= EICRx_BIT;
    299 #endif
    300 }
    301 
    302 // Used by the sender to synchronize timing with the reciver.
    303 static void sync_recv(void) NO_INLINE;
    304 static
    305 void sync_recv(void) {
    306   for (uint8_t i = 0; i < SERIAL_DELAY*5 && serial_read_pin(); i++ ) {
    307   }
    308   // This shouldn't hang if the target disconnects because the
    309   // serial line will float to high if the target does disconnect.
    310   while (!serial_read_pin());
    311 }
    312 
    313 // Used by the reciver to send a synchronization signal to the sender.
    314 static void sync_send(void) NO_INLINE;
    315 static
    316 void sync_send(void) {
    317   serial_low();
    318   serial_delay();
    319   serial_high();
    320 }
    321 
    322 // Reads a byte from the serial line
    323 static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) NO_INLINE;
    324 static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) {
    325     uint8_t byte, i, p, pb;
    326 
    327   _delay_sub_us(READ_WRITE_START_ADJUST);
    328   for( i = 0, byte = 0, p = PARITY; i < bit; i++ ) {
    329       serial_delay_half1();   // read the middle of pulses
    330       if( serial_read_pin() ) {
    331           byte = (byte << 1) | 1; p ^= 1;
    332       } else {
    333           byte = (byte << 1) | 0; p ^= 0;
    334       }
    335       _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
    336       serial_delay_half2();
    337   }
    338   /* recive parity bit */
    339   serial_delay_half1();   // read the middle of pulses
    340   pb = serial_read_pin();
    341   _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
    342   serial_delay_half2();
    343 
    344   *pterrcount += (p != pb)? 1 : 0;
    345 
    346   return byte;
    347 }
    348 
    349 // Sends a byte with MSB ordering
    350 void serial_write_chunk(uint8_t data, uint8_t bit) NO_INLINE;
    351 void serial_write_chunk(uint8_t data, uint8_t bit) {
    352     uint8_t b, p;
    353     for( p = PARITY, b = 1<<(bit-1); b ; b >>= 1) {
    354         if(data & b) {
    355             serial_high(); p ^= 1;
    356         } else {
    357             serial_low();  p ^= 0;
    358         }
    359         serial_delay();
    360     }
    361     /* send parity bit */
    362     if(p & 1) { serial_high(); }
    363     else      { serial_low(); }
    364     serial_delay();
    365 
    366     serial_low(); // sync_send() / senc_recv() need raise edge
    367 }
    368 
    369 static void serial_send_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
    370 static
    371 void serial_send_packet(uint8_t *buffer, uint8_t size) {
    372   for (uint8_t i = 0; i < size; ++i) {
    373     uint8_t data;
    374     data = buffer[i];
    375     sync_send();
    376     serial_write_chunk(data,8);
    377   }
    378 }
    379 
    380 static uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
    381 static
    382 uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) {
    383   uint8_t pecount = 0;
    384   for (uint8_t i = 0; i < size; ++i) {
    385     uint8_t data;
    386     sync_recv();
    387     data = serial_read_chunk(&pecount, 8);
    388     buffer[i] = data;
    389   }
    390   return pecount == 0;
    391 }
    392 
    393 inline static
    394 void change_sender2reciver(void) {
    395     sync_send();          //0
    396     serial_delay_half1(); //1
    397     serial_low();         //2
    398     serial_input_with_pullup(); //2
    399     serial_delay_half1(); //3
    400 }
    401 
    402 inline static
    403 void change_reciver2sender(void) {
    404     sync_recv();     //0
    405     serial_delay();  //1
    406     serial_low();    //3
    407     serial_output(); //3
    408     serial_delay_half1(); //4
    409 }
    410 
    411 static inline uint8_t nibble_bits_count(uint8_t bits)
    412 {
    413     bits = (bits & 0x5) + (bits >> 1 & 0x5);
    414     bits = (bits & 0x3) + (bits >> 2 & 0x3);
    415     return bits;
    416 }
    417 
    418 // interrupt handle to be used by the target device
    419 ISR(SERIAL_PIN_INTERRUPT) {
    420 
    421 #ifndef SERIAL_USE_MULTI_TRANSACTION
    422   serial_low();
    423   serial_output();
    424   SSTD_t *trans = Transaction_table;
    425 #else
    426   // recive transaction table index
    427   uint8_t tid, bits;
    428   uint8_t pecount = 0;
    429   sync_recv();
    430   bits = serial_read_chunk(&pecount,7);
    431   tid = bits>>3;
    432   bits = (bits&7) != nibble_bits_count(tid);
    433   if( bits || pecount> 0 || tid > Transaction_table_size ) {
    434       return;
    435   }
    436   serial_delay_half1();
    437 
    438   serial_high(); // response step1 low->high
    439   serial_output();
    440   _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT*SLAVE_INT_ACK_WIDTH);
    441   SSTD_t *trans = &Transaction_table[tid];
    442   serial_low(); // response step2 ack high->low
    443 #endif
    444 
    445   // target send phase
    446   if( trans->target2initiator_buffer_size > 0 )
    447       serial_send_packet((uint8_t *)trans->target2initiator_buffer,
    448                          trans->target2initiator_buffer_size);
    449   // target switch to input
    450   change_sender2reciver();
    451 
    452   // target recive phase
    453   if( trans->initiator2target_buffer_size > 0 ) {
    454       if (serial_recive_packet((uint8_t *)trans->initiator2target_buffer,
    455                                trans->initiator2target_buffer_size) ) {
    456           *trans->status = TRANSACTION_ACCEPTED;
    457       } else {
    458           *trans->status = TRANSACTION_DATA_ERROR;
    459       }
    460   } else {
    461       *trans->status = TRANSACTION_ACCEPTED;
    462   }
    463 
    464   sync_recv(); //weit initiator output to high
    465 }
    466 
    467 /////////
    468 //  start transaction by initiator
    469 //
    470 // int  soft_serial_transaction(int sstd_index)
    471 //
    472 // Returns:
    473 //    TRANSACTION_END
    474 //    TRANSACTION_NO_RESPONSE
    475 //    TRANSACTION_DATA_ERROR
    476 // this code is very time dependent, so we need to disable interrupts
    477 #ifndef SERIAL_USE_MULTI_TRANSACTION
    478 int  soft_serial_transaction(void) {
    479   SSTD_t *trans = Transaction_table;
    480 #else
    481 int  soft_serial_transaction(int sstd_index) {
    482   if( sstd_index > Transaction_table_size )
    483       return TRANSACTION_TYPE_ERROR;
    484   SSTD_t *trans = &Transaction_table[sstd_index];
    485 #endif
    486   cli();
    487 
    488   // signal to the target that we want to start a transaction
    489   serial_output();
    490   serial_low();
    491   _delay_us(SLAVE_INT_WIDTH_US);
    492 
    493 #ifndef SERIAL_USE_MULTI_TRANSACTION
    494   // wait for the target response
    495   serial_input_with_pullup();
    496   _delay_us(SLAVE_INT_RESPONSE_TIME);
    497 
    498   // check if the target is present
    499   if (serial_read_pin()) {
    500     // target failed to pull the line low, assume not present
    501     serial_output();
    502     serial_high();
    503     *trans->status = TRANSACTION_NO_RESPONSE;
    504     sei();
    505     return TRANSACTION_NO_RESPONSE;
    506   }
    507 
    508 #else
    509   // send transaction table index
    510   int tid = (sstd_index<<3) | (7 & nibble_bits_count(sstd_index));
    511   sync_send();
    512   _delay_sub_us(TID_SEND_ADJUST);
    513   serial_write_chunk(tid, 7);
    514   serial_delay_half1();
    515 
    516   // wait for the target response (step1 low->high)
    517   serial_input_with_pullup();
    518   while( !serial_read_pin() ) {
    519       _delay_sub_us(2);
    520   }
    521 
    522   // check if the target is present (step2 high->low)
    523   for( int i = 0; serial_read_pin(); i++ ) {
    524       if (i > SLAVE_INT_ACK_WIDTH + 1) {
    525           // slave failed to pull the line low, assume not present
    526           serial_output();
    527           serial_high();
    528           *trans->status = TRANSACTION_NO_RESPONSE;
    529           sei();
    530           return TRANSACTION_NO_RESPONSE;
    531       }
    532       _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT);
    533   }
    534 #endif
    535 
    536   // initiator recive phase
    537   // if the target is present syncronize with it
    538   if( trans->target2initiator_buffer_size > 0 ) {
    539       if (!serial_recive_packet((uint8_t *)trans->target2initiator_buffer,
    540                                 trans->target2initiator_buffer_size) ) {
    541           serial_output();
    542           serial_high();
    543           *trans->status = TRANSACTION_DATA_ERROR;
    544           sei();
    545           return TRANSACTION_DATA_ERROR;
    546       }
    547    }
    548 
    549   // initiator switch to output
    550   change_reciver2sender();
    551 
    552   // initiator send phase
    553   if( trans->initiator2target_buffer_size > 0 ) {
    554       serial_send_packet((uint8_t *)trans->initiator2target_buffer,
    555                          trans->initiator2target_buffer_size);
    556   }
    557 
    558   // always, release the line when not in use
    559   sync_send();
    560 
    561   *trans->status = TRANSACTION_END;
    562   sei();
    563   return TRANSACTION_END;
    564 }
    565 
    566 #ifdef SERIAL_USE_MULTI_TRANSACTION
    567 int soft_serial_get_and_clean_status(int sstd_index) {
    568     SSTD_t *trans = &Transaction_table[sstd_index];
    569     cli();
    570     int retval = *trans->status;
    571     *trans->status = 0;;
    572     sei();
    573     return retval;
    574 }
    575 #endif
    576 
    577 #endif
    578 
    579 // Helix serial.c history
    580 //   2018-1-29 fork from let's split and add PD2, modify sync_recv() (#2308, bceffdefc)
    581 //   2018-6-28 bug fix master to slave comm and speed up (#3255, 1038bbef4)
    582 //             (adjusted with avr-gcc 4.9.2)
    583 //   2018-7-13 remove USE_SERIAL_PD2 macro (#3374, f30d6dd78)
    584 //             (adjusted with avr-gcc 4.9.2)
    585 //   2018-8-11 add support multi-type transaction (#3608, feb5e4aae)
    586 //             (adjusted with avr-gcc 4.9.2)
    587 //   2018-10-21 fix serial and RGB animation conflict (#4191, 4665e4fff)
    588 //             (adjusted with avr-gcc 7.3.0)
    589 //   2018-10-28 re-adjust compiler depend value of delay (#4269, 8517f8a66)
    590 //             (adjusted with avr-gcc 5.4.0, 7.3.0)