]> git.tdb.fi Git - model-railway-devices.git/blob - arducontrol/s88.c
Avoid a race condition with the last bytes of an S88 read
[model-railway-devices.git] / arducontrol / s88.c
1 #include <avr/io.h>
2 #include "interface.h"
3 #include "ringbuffer.h"
4 #include "s88.h"
5 #include "serial.h"
6 #include "timer.h"
7
8 #define RESET PORTD4
9 #define LOAD PORTD5
10 #define CLOCK PORTD6
11 #define DATA PIN7
12 #define BIT(x) (1<<(x))
13
14 static volatile uint8_t s88_read_count = 0;
15 static uint8_t s88_read_bit = 0;
16 static volatile uint8_t s88_read_phase = 0;
17 static uint8_t s88_data = 0;
18 static RINGBUFFER(s88_buffer, 8);
19 static uint8_t s88_out_index = 0;
20
21 void s88_init(void)
22 {
23         DDRD = (DDRD&0x0F)|0x70;
24         PORTD &= 0x0F;
25
26         timer_start_hz(0, 80000, 1);
27 }
28
29 void s88_check(void)
30 {
31         /* Check remaining read count before ringbuffer fill.  If it is zero, no
32         more bytes are arriving in the buffer and the contents can be sent out. */
33         uint8_t rc = s88_read_count;
34         uint8_t count = ringbuffer_fill(s88_buffer);
35         if(count>=4 || (count>0 && !rc))
36         {
37                 uint8_t reply[10];
38
39                 reply[0] = S88_DATA;
40                 reply[1] = s88_out_index;
41                 for(uint8_t i=0; i<count; ++i)
42                         reply[2+i] = ringbuffer_pop(s88_buffer);
43                 interface_send(reply, 2+count);
44                 s88_out_index += count;
45         }
46 }
47
48 uint8_t s88_command(const uint8_t *cmd_buf, uint8_t cmd_length)
49 {
50         if(cmd_buf[0]==S88_READ)
51         {
52                 if(cmd_length!=2)
53                         return LENGTH_ERROR;
54
55                 while(s88_read_count || s88_read_phase) ;
56
57                 s88_out_index = 0;
58                 s88_read_phase = 0xF0;
59                 s88_read_count = cmd_buf[1];
60         }
61         else
62                 return INVALID_COMMAND;
63
64         return COMMAND_OK;
65 }
66
67 static inline void s88_tick(void)
68 {
69         if(s88_read_phase==0)
70         {
71                 if(!s88_read_count || ringbuffer_space(s88_buffer)==0)
72                         return;
73
74                 PORTD |= BIT(CLOCK);
75         }
76         else if(s88_read_phase==1)
77         {
78                 uint8_t bit = (PIND>>7)&1;
79                 s88_data = (s88_data<<1)|bit;
80                 ++s88_read_bit;
81                 if(s88_read_bit==8)
82                 {
83                         ringbuffer_push(s88_buffer, s88_data);
84
85                         --s88_read_count;
86                         s88_read_bit = 0;
87                 }
88         }
89         else if(s88_read_phase==2)
90                 PORTD &= ~BIT(CLOCK);
91         else if(s88_read_phase>3)
92         {
93                 if(s88_read_phase==0xF0)
94                         PORTD |= BIT(LOAD);
95                 else if(s88_read_phase==0xF4)
96                         PORTD |= BIT(CLOCK);
97                 else if(s88_read_phase==0xF6)
98                         PORTD &= ~BIT(CLOCK);
99                 else if(s88_read_phase==0xFA)
100                         PORTD |= BIT(RESET);
101                 else if(s88_read_phase==0xFC)
102                         PORTD &= ~BIT(RESET);
103                 else if(s88_read_phase==0xFF)
104                 {
105                         s88_data = (PIND>>7)&1;
106                         ++s88_read_bit;
107                         PORTD &= ~BIT(LOAD);
108                 }
109                 ++s88_read_phase;
110                 return;
111         }
112
113         s88_read_phase = (s88_read_phase+1)&3;
114 }
115
116 TIMER_SET_CALLBACK(0, s88_tick)