aboutsummaryrefslogtreecommitdiff
path: root/arduino/ace/ace.c
blob: ea05e1c6235172b6b98af8c240d7e6be347e8a7c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include "ace.h"

#include <avr/interrupt.h>


typedef struct {
  uint8_t buffer[SERIAL_BUFFER_SIZE];
  volatile uint16_t head;
  volatile uint16_t tail;
} ring_buffer;

static ring_buffer tx_buffer0 = {{0}, 0, 0};
static framer framer0;
static arq arq0;
static uint32_t max_ticks0;
static uint32_t ticks0;

//initialize UART
static void init_s0(uint32_t baud) {
  UCSR0A |= (1 << U2X0);   //enable double speed transmission
  uint16_t baud_setting = (F_CPU / 4 / baud - 1) / 2;
  UBRR0H = baud_setting >> 8;
  UBRR0L = baud_setting;
  UCSR0B |= (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0);   // defaults to 8-bit, no parity, 1 stop bit
  UCSR0B &= ~(1 << UDRIE0) | (1 << TXEN0) | (1 << RXCIE0);
}

static void send_to_s0(uint8_t byte) {
  uint16_t i = (tx_buffer0.head + 1) % SERIAL_BUFFER_SIZE;
  // If the output buffer is full, there's nothing for it other than to 
  // wait for the interrupt handler to empty it a bit
  if (i == tx_buffer0.tail) return;
  //while (i == tx_buffer0.tail) {};
  tx_buffer0.buffer[tx_buffer0.head] = byte;
  tx_buffer0.head = i;
	//enable data register empty interrupt
	UCSR0B |= (1 << UDRIE0);
}

static void send_to_framer0(int16_t size, const uint8_t* const data) {
  send_frame(&framer0, size, data);
}

static void receive_from_framer0(int16_t size, uint8_t* data) {
  receive_frame(&arq0, size, data);
}

//called when byte is received
ISR(USART0_RX_vect) {
  uint8_t c = UDR0;
  receive_byte(&framer0, c);
}

//called when data register is empty
ISR(USART0_UDRE_vect) {
  if (tx_buffer0.head == tx_buffer0.tail) {
    UCSR0B &= ~(1 << UDRIE0); //buffer empty, disable interruot
  } else {
    uint8_t c = tx_buffer0.buffer[tx_buffer0.tail];
    tx_buffer0.tail = (tx_buffer0.tail + 1) % SERIAL_BUFFER_SIZE;
    UDR0 = c; //write next byte
  }
}

//initialize timer
static void init_t0(uint32_t ticks) {
  cli();
  TCCR1A = 0;
  TCCR1B = 0;
  TCCR1B |= (1 << WGM12); // turn on CTC mode:
  TCCR1B |= (1 << CS11); // set CS31 for 64 prescaler
  // set compare match register to desired timer count:
  OCR1A = F_CPU / 1000 / 64; // should be 250 for F_CPU=16Mhz and f = 1000Hz
  sei();
  //the timer should now interrupt every ms
  max_ticks0 = ticks;
  ticks0 = 0;
}

inline static void start_t0() {
  TIMSK1 |= (1 << OCIE1A);
}

inline static void stop_t0() {
  TIMSK1 &= ~(1 << OCIE1A);
}

ISR(TIMER1_COMPA_vect) {
  ticks0 += 1;
  if (ticks0 > max_ticks0) {
    timeout(&arq0);
    ticks0 = 0;
  }
} 

void init_ace0(uint32_t baud, uint32_t extra_time) {
  init_s0(baud);
  init_framer(&framer0);
  init_arq(&arq0);
  
  framer0.sender = send_to_s0;
  arq0.sender = send_to_framer0;
  
  framer0.receiver = receive_from_framer0;
  arq0.event_handler = ace_event0;
  
  uint32_t bits = (MAX_FRAME_SIZE + 3) * 8 * 2;
  uint32_t tx_time = bits * 1000 / baud; // transmission time in milliseconds
  init_t0(tx_time + extra_time);
  arq0.start_timer = start_t0;
  arq0.stop_timer = stop_t0;
}

void ace_send0(uint8_t size, const uint8_t* const message) {
  send_message(&arq0, size, message);
}