]> git.tdb.fi Git - model-railway-devices.git/blobdiff - arducontrol/s88.c
Add support for S88 feedback modules
[model-railway-devices.git] / arducontrol / s88.c
diff --git a/arducontrol/s88.c b/arducontrol/s88.c
new file mode 100644 (file)
index 0000000..1ce5740
--- /dev/null
@@ -0,0 +1,110 @@
+#include <avr/io.h>
+#include "interface.h"
+#include "ringbuffer.h"
+#include "s88.h"
+#include "serial.h"
+#include "timer.h"
+
+#define RESET PORTD4
+#define LOAD PORTD5
+#define CLOCK PORTD6
+#define DATA PIN7
+#define BIT(x) (1<<(x))
+
+volatile uint8_t s88_read_count = 0;
+uint8_t s88_read_bit = 0;
+volatile uint8_t s88_read_phase = 0;
+uint8_t s88_data = 0;
+RINGBUFFER(s88_buffer, 8);
+uint8_t s88_out_index = 0;
+
+void s88_init(void)
+{
+       DDRD = (DDRD&0x0F)|0x70;
+       PORTD &= 0x0F;
+
+       timer_start_hz(0, 80000, 1);
+}
+
+void s88_check(void)
+{
+       // Only send one packet per check to avoid blocking
+       if(ringbuffer_fill(s88_buffer)>0)
+       {
+               uint8_t reply[3];
+               reply[0] = S88_DATA;
+               reply[1] = s88_out_index++;
+               reply[2] = ringbuffer_pop(s88_buffer);
+               interface_send(reply, sizeof(reply));
+       }
+}
+
+uint8_t s88_command(const uint8_t *cmd_buf, uint8_t cmd_length)
+{
+       if(cmd_buf[0]==S88_READ)
+       {
+               if(cmd_length!=2)
+                       return LENGTH_ERROR;
+
+               while(s88_read_count || s88_read_phase) ;
+
+               s88_out_index = 0;
+               s88_read_phase = 0xF0;
+               s88_read_count = cmd_buf[1];
+       }
+       else
+               return INVALID_COMMAND;
+
+       return COMMAND_OK;
+}
+
+static inline void s88_tick(void)
+{
+       if(s88_read_phase==0)
+       {
+               if(!s88_read_count || ringbuffer_space(s88_buffer)==0)
+                       return;
+
+               PORTD |= BIT(CLOCK);
+       }
+       else if(s88_read_phase==1)
+       {
+               uint8_t bit = (PIND>>7)&1;
+               s88_data = (s88_data<<1)|bit;
+               ++s88_read_bit;
+               if(s88_read_bit==8)
+               {
+                       ringbuffer_push(s88_buffer, s88_data);
+
+                       --s88_read_count;
+                       s88_read_bit = 0;
+               }
+       }
+       else if(s88_read_phase==2)
+               PORTD &= ~BIT(CLOCK);
+       else if(s88_read_phase>3)
+       {
+               if(s88_read_phase==0xF0)
+                       PORTD |= BIT(LOAD);
+               else if(s88_read_phase==0xF4)
+                       PORTD |= BIT(CLOCK);
+               else if(s88_read_phase==0xF6)
+                       PORTD &= ~BIT(CLOCK);
+               else if(s88_read_phase==0xFA)
+                       PORTD |= BIT(RESET);
+               else if(s88_read_phase==0xFC)
+                       PORTD &= ~BIT(RESET);
+               else if(s88_read_phase==0xFF)
+               {
+                       s88_data = (PIND>>7)&1;
+                       ++s88_read_bit;
+                       PORTD &= ~BIT(LOAD);
+               }
+               ++s88_read_phase;
+               return;
+       }
+
+       s88_read_phase = (s88_read_phase+1)&3;
+}
+
+TIMER_SET_CALLBACK(0, s88_tick)