whitespace editing / typo correction
[labortage2013badge.git] / firmware / percnt2.c
1 /*
2  * 
3  * author: Daniel Otte
4  * email:  daniel.otte@rub.de
5  * license: GPLv3
6  * 
7  */
8  
9
10 #include <stdlib.h> /* labs() */
11 #include <stdint.h>
12 #include <avr/eeprom.h>
13 #include <avr/pgmspace.h>
14
15 #ifndef PERCNT_NO
16  #define PERCNT_NO 1
17 #endif
18
19 #ifndef PERCNT_BPC /* bytes per counter */
20  #define PERCNT_BPC 16
21 #endif
22
23 #define PERCNT_MBS ((PERCNT_BPC + 7) / 8) /* mask block size */
24
25 #ifndef PERCNT_CS
26  #define PERCNT_CS 4 /* counter size (max 4)*/
27 #endif
28
29 #define PERCNT_RESETONERROR
30
31 #ifndef PERCNT_POLICY_INC
32  #ifndef PERCNT_POLICY_STAY
33  #define PERCNT_POLICY_INC
34 // #error You have to specify a counter policy either PERCNT_POLICY_STAY or PERCNT_POLICY_INC
35  #endif
36 #endif
37
38 #ifdef PERCNT_POLICY_INC
39  #ifdef PERCNT_POLICY_STAY
40  #error You have to specify a counter policy either PERCNT_POLICY_STAY or PERCNT_POLICY_INC (not both)
41  #endif
42 #endif
43
44 uint8_t percnt_count[PERCNT_NO][3][PERCNT_BPC] EEMEM;
45
46 uint8_t percnt_mb[PERCNT_NO][3][PERCNT_MBS] EEMEM;
47
48 typedef enum __attribute__((packed)) {counterA = 0, counterB = 1, counterC = 2} percnt_active_t;
49
50 percnt_active_t percnt_active[PERCNT_NO];
51
52 #ifdef PERCNT_CACHE
53 uint32_t percnt_cache[PERCNT_NO];
54 #endif
55
56 /**
57  * this function resets the counter given in the parameter back to zero
58  *  this is quite unsafe
59  */
60 void percnt_reset(uint8_t counter) {
61         uint8_t i;
62         for (i = 0; i < PERCNT_BPC; ++i) {
63                 eeprom_write_byte(&(percnt_count[counter][counterA][i]), 0);
64         }
65         eeprom_write_byte(&(percnt_count[counter][counterB][0]), 1);
66         for (i = 1; i < PERCNT_BPC; ++i) {
67                 eeprom_write_byte(&(percnt_count[counter][counterB][i]), 0);
68         }
69         eeprom_write_byte(&(percnt_count[counter][counterC][0]), 2);
70         for (i = 1; i < PERCNT_BPC; ++i) {
71                 eeprom_write_byte(&(percnt_count[counter][counterC][i]), 0);
72         }
73         for (i = 0; i < PERCNT_MBS; ++i) {
74                 eeprom_write_byte(&(percnt_mb[counter][counterA][i]), 0);
75         }
76         for (i = 0; i < PERCNT_MBS; ++i) {
77                 eeprom_write_byte(&(percnt_mb[counter][counterB][i]), 0);
78         }
79         for (i = 0; i < PERCNT_MBS; ++i) {
80                 eeprom_write_byte(&(percnt_mb[counter][counterC][i]), 0);
81         }
82         percnt_active[counter] = counterC;
83 }
84
85
86 static const uint8_t bitcnt_lut_P[16] PROGMEM =
87     { 0, 1, 1, 2,  /* 0..3 */
88       1, 2, 2, 3,  /* 4..7 */
89       1, 2, 2, 3,  /* 8..B */
90       2, 3, 3, 4} ; /* C..F */
91
92
93 static
94 uint8_t bitcnt(const void* a, uint8_t length){
95         uint8_t ret = 0;
96         while (length--) {
97                 ret += pgm_read_byte(&bitcnt_lut_P[(*((uint8_t*)a)) >> 4]);
98                 ret += pgm_read_byte(&bitcnt_lut_P[(*((uint8_t*)a)) & 0x0f]);
99         }
100         return ret;
101 }
102
103 /**
104  * returns 0 if everything went right, 1 or even 2 in the case of failure
105  */
106 static
107 uint8_t percnt_writecntx(uint32_t v, uint8_t counter, percnt_active_t subc){
108         uint8_t maskb[PERCNT_MBS];
109         uint8_t free;
110         uint8_t i, j = 0, t;
111         eeprom_read_block(maskb, &(percnt_mb[counter][subc][0]), PERCNT_MBS);
112         free = PERCNT_BPC - bitcnt(maskb, PERCNT_MBS);
113         
114         for (i = 0; i < PERCNT_CS; ++i) {
115                 if (free < PERCNT_CS) {
116                         /* we don't have enough bytes left, so we can't count anymore :-( */
117                         return 1;
118                 }
119                 /* get next usable byte */
120                 while ((maskb[j / 8] >> (j % 8)) & 1)
121                         ++j;
122                 t = eeprom_read_byte(&(percnt_count[counter][subc][j]));
123                 if(t != ((uint8_t*)&v)[i]){
124                         /* we have to do a write */
125                         eeprom_write_byte(&(percnt_count[counter][subc][j]), ((uint8_t*)&v)[i]);
126                         /* and check back */
127                         t = eeprom_read_byte(&(percnt_count[counter][subc][j]));
128                         if(t != ((uint8_t*)&v)[i]){
129                                 /* thats not good, readback failed */
130                                 /* mark byte as bad */
131                                 maskb[j / 8] |= 1 << (j % 8);
132                                 eeprom_write_byte(&(percnt_mb[counter][subc][j / 8]), maskb[j / 8]);
133                                 /* check back the maskblock write*/
134                                 t = eeprom_read_byte(&(percnt_mb[counter][subc][j / 8]));
135                                 if(t != maskb[j / 8]){
136                                         /* uargghh! that should not happen, our bad byte table has a bad byte*/
137                                         return 2;
138                                 }
139                                 --i; /* this makes this byte gets another try */
140                         }       
141                 }
142                 ++j;
143         }
144         return 0;
145 }
146
147 /******************************************************************************/
148
149 static
150 uint32_t percnt_readcntx(uint8_t counter, percnt_active_t subc){
151         uint32_t ret = 0;
152         uint8_t maskb[PERCNT_MBS];
153         uint8_t i, j = 0;
154         eeprom_read_block(maskb, &(percnt_mb[counter][subc][0]), PERCNT_MBS);
155         for (i = 0; i < PERCNT_CS; ++i) {
156                 while (((maskb[j / 8]) >> (j % 8)) & 1)
157                         ++j;
158                 ((uint8_t*)&ret)[i] = eeprom_read_byte(&(percnt_count[counter][subc][j]));
159                 ++j;
160         }
161         return ret;
162 }
163
164 /******************************************************************************/
165
166 uint32_t percnt_get(uint8_t counter) {
167         return percnt_readcntx(counter, percnt_active[counter]) - 2;
168 }
169
170 /******************************************************************************/
171
172 uint8_t percnt_inc(uint8_t counter) {
173         uint32_t t;
174         t = percnt_get(counter);
175         percnt_active[counter] += 1;
176         if (percnt_active[counter] == 3)
177                 percnt_active[counter] = 0;
178         return percnt_writecntx(t + 1 + 2, counter, percnt_active[counter]); /* remember, _get subtracts two */
179 }
180
181 /******************************************************************************/
182
183 uint8_t percnt_init(uint8_t counter){
184         uint32_t a,b,c;
185         uint32_t dab, dac, dbc;
186         a = percnt_readcntx(counter, counterA);
187         b = percnt_readcntx(counter, counterB);
188         c = percnt_readcntx(counter, counterC);
189         dab = labs(a - b);
190         dac = labs(a - c);
191         dbc = labs(b - c);
192         if (dab == 1 && dbc == 1 && dac == 2) {
193                 percnt_active[counter] = counterC;
194                 return 0;
195         }
196         if(dab == 1 && dbc == 2 && dac == 1) {
197                 percnt_active[counter] = counterB;
198                 return 0;
199         }
200         if(dab == 2 && dbc == 1 && dac == 1){
201                 percnt_active[counter] = counterA;
202                 return 0;
203         }
204         
205         /* here it gets intresting */
206         if(dab == 1 || dac == 1 || dbc == 1){
207                 /* we might got interrupted while incrementing */
208                 /* action depends on policy either finish the increment or not */
209                 #ifdef PERCNT_POLICY_STAY
210                 if(dab == 1){
211                         percnt_active[counter]=(a < b) ? counterB : counterA;
212                         c = (a < b) ? (a - 1) : (b - 1);
213                         return percnt_writecntx(c, counter, counterC);
214                 }
215                 if(dac == 1){
216                         percnt_active[counter]=(a<c)?counterC:counterA;
217                         b = (a < c) ? (a - 1) : (c - 1);
218                         return percnt_writecntx(b, counter, counterB);
219                 }
220                 if(dbc == 1){
221                         percnt_active[counter]=(b<c)?counterC:counterB;
222                         a = (b < c) ? (b - 1) : (c - 1);
223                         return percnt_writecntx(a, counter, counterA);
224                 }
225                 #endif
226                 #ifdef PERCNT_POLICY_INC
227                 if(dab == 1){
228                         percnt_active[counter] = counterC;
229                         c = (a < b) ? (a - 1) : (b - 1);
230                         return percnt_writecntx(c + 1, counter, counterC);
231                 }
232                 if(dac == 1){
233                         percnt_active[counter] = counterB;
234                         b = (a < c) ? (a - 1) : (c - 1);
235                         return percnt_writecntx(b + 1, counter, counterB);
236                 }
237                 if(dbc == 1){
238                         percnt_active[counter] = counterA;
239                         a = (b < c) ? (b - 1) : (c - 1);
240                         return percnt_writecntx(a + 1, counter, counterA);
241                 }
242                 #endif
243         } else {
244                 /* something really strange happened */
245                 /* may be we have to initialize or so, but we must make sure that no one evil drives us here */
246                 #ifdef PERCNT_RESETONERROR
247                 percnt_reset(counter);
248                 return 0;
249                 #endif
250                 
251                 return 23;
252         }
253         /* we won't get here, but to keep the compile quiet: */
254         return 42;
255 }
256
257
258
259
260