ucmb: Add more documentation and example code for AVR8
[packages.git] / utils / ucmb / microcontroller_examples / atmel_avr8 / ucmb.c
1 /*
2  *   Microcontroller message bus
3  *   uc-side slave implementation for Atmel AVR8
4  *
5  *   The gcc compiler always treats multi-byte variables as litte-endian.
6  *   So no explicit endianness conversions are done on the message header,
7  *   footer and status data structures.
8  *
9  *   Copyright (C) 2009 Michael Buesch <mb@bu3sch.de>
10  *
11  *   This program is free software; you can redistribute it and/or
12  *   modify it under the terms of the GNU General Public License
13  *   as published by the Free Software Foundation; either version 2
14  *   of the License, or (at your option) any later version.
15  *
16  *   This program is distributed in the hope that it will be useful,
17  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *   GNU General Public License for more details.
20  */
21
22 #include "ucmb.h"
23
24 #include <stdint.h>
25 #include <avr/io.h>
26 #include <avr/interrupt.h>
27 #include <util/crc16.h>
28
29
30 struct ucmb_message_hdr {
31         uint16_t magic;         /* UCMB_MAGIC */
32         uint16_t len;           /* Payload length (excluding header) */
33 } __attribute__((packed));
34
35 struct ucmb_message_footer {
36         uint16_t crc;           /* CRC of the header + payload. */
37 } __attribute__((packed));
38
39 struct ucmb_status {
40         uint16_t magic;         /* UCMB_MAGIC */
41         uint16_t code;          /* enum ucmb_status_code */
42 } __attribute__((packed));
43
44 #define UCMB_MAGIC              0x1337
45
46 enum ucmb_status_code {
47         UCMB_STAT_OK = 0,
48         UCMB_STAT_EPROTO,       /* Protocol format error */
49         UCMB_STAT_ENOMEM,       /* Out of memory */
50         UCMB_STAT_E2BIG,        /* Message too big */
51         UCMB_STAT_ECRC,         /* CRC error */
52 };
53
54
55 static uint8_t ucmb_buf[sizeof(struct ucmb_message_hdr) +
56                         UCMB_MAX_MSG_LEN +
57                         sizeof(struct ucmb_message_footer)];
58 static uint16_t ucmb_buf_ptr;
59 static struct ucmb_status status_buf;
60 static uint16_t ucmb_send_message_len;
61
62 /* Statemachine */
63 static uint8_t ucmb_state;
64 enum {
65         UCMB_ST_LISTEN,
66         UCMB_ST_SENDSTATUS,
67         UCMB_ST_SENDMESSAGE,
68 };
69
70 #define TRAILING        1
71
72
73 static void ucmb_send_next_byte(void)
74 {
75         switch (ucmb_state) {
76         case UCMB_ST_SENDSTATUS: {
77                 const uint8_t *st = (const uint8_t *)&status_buf;
78
79                 if (ucmb_buf_ptr < sizeof(struct ucmb_status))
80                         SPDR = st[ucmb_buf_ptr];
81                 ucmb_buf_ptr++;
82                 if (ucmb_buf_ptr == sizeof(struct ucmb_status) + TRAILING) {
83                         ucmb_buf_ptr = 0;
84                         if (ucmb_send_message_len) {
85                                 ucmb_state = UCMB_ST_SENDMESSAGE;
86                                 goto st_sendmessage;
87                         } else
88                                 ucmb_state = UCMB_ST_LISTEN;
89                 }
90                 break;
91         }
92         case UCMB_ST_SENDMESSAGE: {
93   st_sendmessage:;
94                 uint16_t full_length = sizeof(struct ucmb_message_hdr) +
95                                        ucmb_send_message_len +
96                                        sizeof(struct ucmb_message_footer);
97                 if (ucmb_buf_ptr < full_length)
98                         SPDR = ucmb_buf[ucmb_buf_ptr];
99                 ucmb_buf_ptr++;
100                 if (ucmb_buf_ptr == full_length + TRAILING) {
101                         ucmb_send_message_len = 0;
102                         ucmb_buf_ptr = 0;
103                         ucmb_state = UCMB_ST_LISTEN;//FIXME retr status
104                 }
105                 break;
106         } }
107 }
108
109 static uint16_t crc16_block_update(uint16_t crc, const void *_data, uint16_t size)
110 {
111         const uint8_t *data = _data;
112
113         while (size) {
114                 crc = _crc16_update(crc, *data);
115                 data++;
116                 size--;
117         }
118
119         return crc;
120 }
121
122 static uint16_t ucmb_calc_msg_buffer_crc(void)
123 {
124         const struct ucmb_message_hdr *hdr;
125         uint16_t crc = 0xFFFF;
126
127         hdr = (const struct ucmb_message_hdr *)ucmb_buf;
128         crc = crc16_block_update(crc, ucmb_buf,
129                                  sizeof(struct ucmb_message_hdr) + hdr->len);
130         crc ^= 0xFFFF;
131
132         return crc;
133 }
134
135 /* SPI data transfer interrupt. */
136 ISR(SPI_STC_vect)
137 {
138         uint8_t data;
139
140         data = SPDR;
141
142         switch (ucmb_state) {
143         case UCMB_ST_LISTEN: {
144                 struct ucmb_message_hdr *hdr;
145                 struct ucmb_message_footer *footer;
146
147                 ucmb_buf[ucmb_buf_ptr++] = data;
148                 if (ucmb_buf_ptr < sizeof(struct ucmb_message_hdr))
149                         return; /* Header RX not complete. */
150                 hdr = (struct ucmb_message_hdr *)ucmb_buf;
151                 if (ucmb_buf_ptr == sizeof(struct ucmb_message_hdr)) {
152                         if (hdr->magic != UCMB_MAGIC) {
153                                 /* Invalid magic! Reset. */
154                                 ucmb_buf_ptr = 0;
155                                 return;
156                         }
157                         if (hdr->len > UCMB_MAX_MSG_LEN) {
158                                 /* Invalid length. */
159                                 //FIXME don't interrupt, but poll len bytes and
160                                 // send an immediate failure report
161                                 ucmb_buf_ptr = 0;
162                                 return;
163                         }
164                         return;
165                 }
166
167                 if (ucmb_buf_ptr == sizeof(struct ucmb_message_hdr) +
168                                     sizeof(struct ucmb_message_footer) +
169                                     hdr->len) {
170                         footer = (struct ucmb_message_footer *)(
171                                         ucmb_buf + sizeof(struct ucmb_message_hdr) +
172                                         hdr->len);
173                         status_buf.magic = UCMB_MAGIC;
174                         status_buf.code = UCMB_STAT_OK;
175                         if (ucmb_calc_msg_buffer_crc() != footer->crc)
176                                 status_buf.code = UCMB_STAT_ECRC;
177                         ucmb_state = UCMB_ST_SENDSTATUS;
178                         ucmb_buf_ptr = 0;
179                         ucmb_send_next_byte();
180
181                         ucmb_send_message_len = ucmb_rx_message(
182                                         ucmb_buf + sizeof(struct ucmb_message_hdr),
183                                         hdr->len);
184                         if (ucmb_send_message_len) {
185                                 footer = (struct ucmb_message_footer *)(
186                                                 ucmb_buf + sizeof(struct ucmb_message_hdr) +
187                                                 ucmb_send_message_len);
188
189                                 hdr->magic = UCMB_MAGIC;
190                                 hdr->len = ucmb_send_message_len;
191                                 footer->crc = ucmb_calc_msg_buffer_crc();
192                         }
193                 }
194                 break;
195         }
196         case UCMB_ST_SENDSTATUS:
197         case UCMB_ST_SENDMESSAGE:
198                 ucmb_send_next_byte();
199                 break;
200         }
201 }
202
203 void ucmb_init(void)
204 {
205         ucmb_state = UCMB_ST_LISTEN;
206
207         /* SPI slave mode 0 with IRQ enabled. */
208         DDRB |= (1 << 4/*MISO*/);
209         DDRB &= ~((1 << 5/*SCK*/) | (1 << 3/*MOSI*/) | (1 << 2/*SS*/));
210         SPCR = (1 << SPE) | (1 << SPIE) /*| (1 << CPOL) | (1 << CPHA)*/;
211         (void)SPSR; /* clear state */
212         (void)SPDR; /* clear state */
213 }