Firmware SDK
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
bc_eeprom.c
1 #include <bc_eeprom.h>
2 #include <bc_irq.h>
3 #include <stm32l0xx.h>
4 #include <bc_tick.h>
5 #include <bc_timer.h>
6 #include <bc_scheduler.h>
7 
8 #define _BC_EEPROM_BASE DATA_EEPROM_BASE
9 #define _BC_EEPROM_END DATA_EEPROM_BANK2_END
10 #define _BC_EEPROM_IS_BUSY() ((FLASH->SR & FLASH_SR_BSY) != 0UL)
11 
12 static struct
13 {
14  bool running;
15  uint32_t address;
16  uint8_t *buffer;
17  size_t length;
18  void (*event_handler)(bc_eepromc_event_t, void *);
19  void *event_param;
20  size_t i;
21  bc_scheduler_task_id_t task_id;
22 
23 } _bc_eeprom;
24 
25 static bool _bc_eeprom_is_busy(bc_tick_t timeout);
26 static void _bc_eeprom_unlock(void);
27 static void _bc_eeprom_lock(void);
28 static bool _bc_eeprom_write(uint32_t address, size_t *i, uint8_t *buffer, size_t length);
29 static void _bc_eeprom_async_write_task(void *param);
30 
31 bool bc_eeprom_write(uint32_t address, const void *buffer, size_t length)
32 {
33  // Add EEPROM base offset to address
34  address += _BC_EEPROM_BASE;
35 
36  // If user attempts to write outside EEPROM area...
37  if ((address + length) > (_BC_EEPROM_END + 1))
38  {
39  // Indicate failure
40  return false;
41  }
42 
43  if (_bc_eeprom_is_busy(50))
44  {
45  return false;
46  }
47 
48  _bc_eeprom_unlock();
49 
50  size_t i = 0;
51 
52  while (i < length)
53  {
54  _bc_eeprom_write(address, &i, (uint8_t *) buffer, length);
55  }
56 
57  _bc_eeprom_lock();
58 
59  // If we do not read what we wrote...
60  if (memcmp(buffer, (void *) address, length) != 0UL)
61  {
62  // Indicate failure
63  return false;
64  }
65 
66  // Indicate success
67  return true;
68 }
69 
70 bool bc_eeprom_async_write(uint32_t address, const void *buffer, size_t length, void (*event_handler)(bc_eepromc_event_t, void *), void *event_param)
71 {
72  if (_bc_eeprom.running)
73  {
74  return false;
75  }
76 
77  _bc_eeprom.address = address += _BC_EEPROM_BASE;
78 
79  // If user attempts to write outside EEPROM area...
80  if ((_bc_eeprom.address + length) > (_BC_EEPROM_END + 1))
81  {
82  // Indicate failure
83  return false;
84  }
85 
86  _bc_eeprom.buffer = (uint8_t *) buffer;
87 
88  _bc_eeprom.length = length;
89 
90  _bc_eeprom.event_handler = event_handler;
91 
92  _bc_eeprom.event_param = event_param;
93 
94  _bc_eeprom.i = 0;
95 
96  _bc_eeprom.task_id = bc_scheduler_register(_bc_eeprom_async_write_task, NULL, 0);
97 
98  _bc_eeprom.running = true;
99 
100  return true;
101 }
102 
104 {
105  if (_bc_eeprom.running)
106  {
107  bc_scheduler_unregister(_bc_eeprom.task_id);
108 
109  _bc_eeprom.running = false;
110  }
111 }
112 
113 bool bc_eeprom_read(uint32_t address, void *buffer, size_t length)
114 {
115  // Add EEPROM base offset to address
116  address += _BC_EEPROM_BASE;
117 
118  // If user attempts to read outside of EEPROM boundary...
119  if ((address + length) > (_BC_EEPROM_END + 1))
120  {
121  // Indicate failure
122  return false;
123  }
124 
125  // Read from EEPROM memory to buffer
126  memcpy(buffer, (void *) address, length);
127 
128  // Indicate success
129  return true;
130 }
131 
132 size_t bc_eeprom_get_size(void)
133 {
134  // Return EEPROM memory size
135  return _BC_EEPROM_END - _BC_EEPROM_BASE + 1;
136 }
137 
138 static bool _bc_eeprom_is_busy(bc_tick_t timeout)
139 {
140  timeout += bc_tick_get();
141 
142  while (_BC_EEPROM_IS_BUSY())
143  {
144  if (timeout > bc_tick_get())
145  {
146  return true;
147  }
148  }
149 
150  return false;
151 }
152 
153 static void _bc_eeprom_unlock(void)
154 {
155  bc_irq_disable();
156 
157  // Unlock FLASH_PECR register
158  if ((FLASH->PECR & FLASH_PECR_PELOCK) != 0)
159  {
160  FLASH->PEKEYR = FLASH_PEKEY1;
161  FLASH->PEKEYR = FLASH_PEKEY2;
162  }
163 
164  bc_irq_enable();
165 }
166 
167 static void _bc_eeprom_lock(void)
168 {
169  bc_irq_disable();
170 
171  // Lock FLASH_PECR register
172  FLASH->PECR |= FLASH_PECR_PELOCK;
173 
174  bc_irq_enable();
175 }
176 
177 static bool _bc_eeprom_write(uint32_t address, size_t *i, uint8_t *buffer, size_t length)
178 {
179  uint32_t addr = address + *i;
180 
181  uint8_t mod = addr % 4;
182 
183  bool write = false;
184 
185  if (mod == 0)
186  {
187  if (*i + 4 > length)
188  {
189  mod = (addr % 2) + 2;
190  }
191  }
192 
193  if (mod == 2)
194  {
195  if (*i + 2 > length)
196  {
197  mod = 1;
198  }
199  }
200 
201  if (mod == 0)
202  {
203  uint32_t value = ((uint32_t) buffer[*i + 3]) << 24 | ((uint32_t) buffer[*i + 2]) << 16 | ((uint32_t) buffer[*i + 1]) << 8 | buffer[*i];
204 
205  if (*((uint32_t *) addr) != value)
206  {
207  *((uint32_t *) addr) = value;
208 
209  write = true;
210  }
211 
212  *i += 4;
213  }
214  else if (mod == 2)
215  {
216  uint16_t value = ((uint16_t) buffer[*i + 1]) << 8 | (uint16_t) buffer[*i];
217 
218  if (*((uint16_t *) addr) != value)
219  {
220  *((uint16_t *) addr) = value;
221 
222  write = true;
223  }
224 
225  *i += 2;
226  }
227  else
228  {
229  uint8_t value = buffer[*i];
230 
231  if (*((uint8_t *) addr) != value)
232  {
233  *((uint8_t *) addr) = value;
234 
235  write = true;
236  }
237 
238  *i += 1;
239  }
240 
241  while (_BC_EEPROM_IS_BUSY())
242  {
243  continue;
244  }
245 
246  return write;
247 }
248 
249 
250 static void _bc_eeprom_async_write_task(void *param)
251 {
252  (void) param;
253 
254  if (_BC_EEPROM_IS_BUSY())
255  {
257 
258  return;
259  }
260 
261  _bc_eeprom_unlock();
262 
263  while(_bc_eeprom.i < _bc_eeprom.length)
264  {
265  if(_bc_eeprom_write(_bc_eeprom.address, &_bc_eeprom.i, _bc_eeprom.buffer, _bc_eeprom.length))
266  {
267  break;
268  }
269  };
270 
271  _bc_eeprom_lock();
272 
273  if(_bc_eeprom.i < _bc_eeprom.length)
274  {
276 
277  return;
278  }
279 
280  _bc_eeprom.running = false;
281 
282  bc_scheduler_unregister(_bc_eeprom.task_id);
283 
284  if (memcmp(_bc_eeprom.buffer, (void *) _bc_eeprom.address, _bc_eeprom.length) != 0UL)
285  {
286  if (_bc_eeprom.event_handler != NULL)
287  {
288  _bc_eeprom.event_handler(BC_EEPROM_EVENT_ASYNC_WRITE_ERROR, _bc_eeprom.event_param);
289  }
290  }
291  else
292  {
293  if (_bc_eeprom.event_handler != NULL)
294  {
295  _bc_eeprom.event_handler(BC_EEPROM_EVENT_ASYNC_WRITE_DONE, _bc_eeprom.event_param);
296  }
297  }
298 }
uint64_t bc_tick_t
Timestamp data type.
Definition: bc_tick.h:16
void bc_irq_enable(void)
Enable interrupt requests globally (call can be nested)
Definition: bc_irq.c:21
bc_scheduler_task_id_t bc_scheduler_register(void(*task)(void *), void *param, bc_tick_t tick)
Register task in scheduler.
Definition: bc_scheduler.c:56
bc_tick_t bc_tick_get(void)
Get absolute timestamp since start of program.
Definition: bc_tick.c:7
EEPROM event sync write error.
Definition: bc_eeprom.h:13
bool bc_eeprom_async_write(uint32_t address, const void *buffer, size_t length, void(*event_handler)(bc_eepromc_event_t, void *), void *event_param)
Async write buffer to EEPROM area and verify it.
Definition: bc_eeprom.c:70
size_t bc_scheduler_task_id_t
Task ID assigned by scheduler.
Definition: bc_scheduler.h:18
bool bc_eeprom_write(uint32_t address, const void *buffer, size_t length)
Write buffer to EEPROM area and verify it.
Definition: bc_eeprom.c:31
EEPROM event sync write done.
Definition: bc_eeprom.h:16
bc_eepromc_event_t
Definition: bc_eeprom.h:10
bool bc_eeprom_read(uint32_t address, void *buffer, size_t length)
Read buffer from EEPROM area.
Definition: bc_eeprom.c:113
size_t bc_eeprom_get_size(void)
Return size of EEPROM area.
Definition: bc_eeprom.c:132
void bc_irq_disable(void)
Disable interrupt requests globally (call can be nested)
Definition: bc_irq.c:7
void bc_scheduler_plan_current_now(void)
Schedule current task for immediate execution.
Definition: bc_scheduler.c:139
void bc_eeprom_async_cancel(void)
Cancel async write.
Definition: bc_eeprom.c:103
void bc_scheduler_unregister(bc_scheduler_task_id_t task_id)
Unregister specified task.
Definition: bc_scheduler.c:80