Smart-Home am Beispiel der Präsenzerkennung im Raum Projektarbeit Lennart Heimbs, Johannes Krug, Sebastian Dohle und Kevin Holzschuh bei Prof. Oliver Hofmann SS2019
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

NVRAM.cpp 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /*
  2. NVRAM.cpp - Byte wise storage for Virtual Pages.
  3. Original Copyright (c) 2017 Frank Holtz. All right reserved.
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with this library; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  15. */
  16. #include "NVRAM.h"
  17. // VirtualPage magic
  18. #define NVRAM_MAGIC (0x7710fdb9)
  19. // Number of emulated cells
  20. #define NVRAM_LENGTH 3072
  21. // Log configuration: Address index in bits
  22. #define NVRAM_ADDR_POS 20
  23. // Log configuration: Mask for comparsion (4k space)
  24. #define NVRAM_ADDR_MASK 0xfff00000
  25. // Log configuration: Bit position of used address bitmap
  26. #define NVRAM_BITMAP_POS 8
  27. // Log configuration: used address bitmap calulation
  28. #define NVRAM_BITMAP_ADDR_SHIFT 8
  29. // Log configuration: Mask for bitmap extraction
  30. #define NVRAM_BITMAP_MASK 0x000fff00
  31. #define ADDR2BIT(index) \
  32. ((1 << (index >> NVRAM_BITMAP_ADDR_SHIFT)) << NVRAM_BITMAP_POS)
  33. NVRAMClass NVRAM;
  34. uint16_t NVRAMClass::length() const
  35. {
  36. return (NVRAM_LENGTH);
  37. }
  38. void NVRAMClass::read_block(uint8_t *dst, uint16_t idx, uint16_t n)
  39. {
  40. uint32_t *vpage;
  41. uint16_t log_start, log_end;
  42. // find correct page
  43. vpage = get_page();
  44. // copy 0xff to dst when no page is available
  45. if (vpage == (uint32_t *)~0) {
  46. for (uint32_t i = 0; i < n; i++) {
  47. ((uint8_t *)dst)[i] = 0xff;
  48. }
  49. return;
  50. }
  51. // calculate actual log position
  52. log_end = get_log_position(vpage);
  53. if (log_end == 0) {
  54. log_start = 1;
  55. } else {
  56. log_start = vpage[0] + 1;
  57. }
  58. /*
  59. Serial.print("\r\nread_block idx=");
  60. Serial.print(idx);
  61. Serial.print(" n=");
  62. Serial.print(n);
  63. Serial.print("("); */
  64. while (n > 0) {
  65. // Read cell
  66. *dst = get_byte_from_page(vpage, log_start, log_end, idx);
  67. // Serial.print(*dst, HEX);
  68. // calculate next address
  69. n--;
  70. dst++;
  71. idx++;
  72. }
  73. // Serial.println(")");
  74. }
  75. uint8_t NVRAMClass::read(const uint16_t idx)
  76. {
  77. uint8_t ret;
  78. read_block(&ret, idx, 1);
  79. return ret;
  80. }
  81. bool NVRAMClass::write_block(uint8_t *src, uint16_t idx, uint16_t n)
  82. {
  83. uint32_t *vpage;
  84. uint32_t bitmap;
  85. uint16_t log_start, log_end;
  86. // find correct page
  87. vpage = get_page();
  88. // return on invalid page
  89. if (vpage == (uint32_t *)~0) {
  90. return false;
  91. }
  92. // calculate actual log position
  93. log_start = vpage[0] + 1;
  94. log_end = get_log_position(vpage);
  95. if (log_end > log_start) {
  96. bitmap = vpage[log_end - 1] & NVRAM_BITMAP_MASK;
  97. } else {
  98. bitmap = 0;
  99. }
  100. while (n > 0) {
  101. // Read cell
  102. uint8_t old_value = get_byte_from_page(vpage, log_start, log_end, idx);
  103. uint8_t new_value = *src;
  104. // Have to write into log?
  105. if (new_value != old_value) {
  106. // need to calculate a new page?
  107. if (log_end >= VirtualPage.length()) {
  108. vpage = switch_page(vpage, &log_start, &log_end);
  109. if (vpage == (uint32_t *)~0) {
  110. // do nothing if no page is available
  111. return false;
  112. }
  113. bitmap = 0;
  114. }
  115. // Add Entry into log
  116. Flash.write(&vpage[log_end], (idx << NVRAM_ADDR_POS) | bitmap |
  117. ADDR2BIT(idx) | (uint32_t)new_value);
  118. log_end++;
  119. }
  120. // calculate next address
  121. n--;
  122. src++;
  123. idx++;
  124. }
  125. return true;
  126. }
  127. bool NVRAMClass::write(uint16_t idx, uint8_t value)
  128. {
  129. return (write_block(&value, idx, 1));
  130. }
  131. int NVRAMClass::write_prepare(uint16_t number)
  132. {
  133. // find correct page
  134. uint32_t *vpage = get_page();
  135. // Want to write to much or into an invalid page?
  136. if ((vpage == (uint32_t *)~0) || (number > length())) {
  137. return -1;
  138. }
  139. // calculate actual log position
  140. uint16_t log_end = get_log_position(vpage);
  141. // Calculate number of free bytes in log
  142. int free_bytes = ((VirtualPage.length() - 1) - log_end);
  143. // switch page when
  144. if (free_bytes < number) {
  145. uint16_t log_start = vpage[0] + 1;
  146. vpage = switch_page(vpage, &log_start, &log_end);
  147. if (vpage == (uint32_t *)~0) {
  148. // do nothing if no page is available
  149. return -1;
  150. }
  151. log_end = get_log_position(vpage);
  152. free_bytes = ((VirtualPage.length() - 1) - log_end);
  153. }
  154. return free_bytes;
  155. }
  156. void NVRAMClass::clean_up(uint16_t write_preserve)
  157. {
  158. VirtualPage.clean_up();
  159. if (write_preserve > 0) {
  160. write_prepare(write_preserve);
  161. }
  162. }
  163. uint32_t *NVRAMClass::switch_page(uint32_t *old_vpage, uint16_t *log_start,
  164. uint16_t *log_end)
  165. {
  166. // Mark old page as in release
  167. VirtualPage.release_prepare(old_vpage);
  168. // Get a blank page
  169. uint32_t *new_vpage = VirtualPage.allocate(NVRAM_MAGIC, VirtualPage.length());
  170. if (new_vpage == (uint32_t *)~0) {
  171. // failed
  172. return new_vpage;
  173. }
  174. // Store four bytes for map creation
  175. uint32_t value;
  176. // Length of new map
  177. uint16_t map_length = 0;
  178. // Build map
  179. #ifdef FLASH_SUPPORTS_RANDOM_WRITE
  180. // Copy current values
  181. for (uint16_t i = 0; i < (NVRAM_LENGTH >> 2); i++) {
  182. read_block((uint8_t *)&value, i << 2, 4);
  183. if (value != (uint32_t)~0) {
  184. // Value found
  185. map_length = i + 1;
  186. Flash.write(&new_vpage[i + 1], value);
  187. }
  188. }
  189. // Store map length
  190. Flash.write(new_vpage, map_length);
  191. #else
  192. // find map length
  193. for (uint16_t i = (NVRAM_LENGTH >> 2); i > 0; i--) {
  194. read_block((uint8_t *)&value, i << 2, 4);
  195. if (value != (uint32_t)~0) {
  196. // Value found
  197. map_length = i;
  198. break;
  199. }
  200. }
  201. map_length++;
  202. // Store map length
  203. Flash.write(new_vpage, map_length);
  204. // Copy current values
  205. for (uint16_t i = 0; i <= map_length; i++) {
  206. read_block((uint8_t *)&value, i << 2, 4);
  207. if (value != (uint32_t)~0) {
  208. // Value found
  209. map_length = i;
  210. Flash.write(&new_vpage[i + 1], value);
  211. }
  212. }
  213. #endif
  214. // Release old page
  215. VirtualPage.release(old_vpage);
  216. // Set log position
  217. *log_start = map_length + 1;
  218. *log_end = *log_start;
  219. return new_vpage;
  220. }
  221. uint32_t *NVRAMClass::get_page()
  222. {
  223. uint32_t *vpage = VirtualPage.get(NVRAM_MAGIC);
  224. // Invalid page?
  225. if (vpage == (uint32_t *)~0) {
  226. // Allocate a new page
  227. vpage = VirtualPage.allocate(NVRAM_MAGIC, VirtualPage.length());
  228. // Set map length to 0
  229. Flash.write(&vpage[0], 0x0);
  230. }
  231. return vpage;
  232. }
  233. uint16_t NVRAMClass::get_log_position(uint32_t *vpage)
  234. {
  235. uint16_t position_min = vpage[0] + 1;
  236. uint16_t position_max = VirtualPage.length();
  237. // Return if page is not filled
  238. if ((vpage[0] == (uint32_t)~0) || (position_min >= position_max)) {
  239. return 0;
  240. }
  241. // loop until postition_min != position_max-1
  242. while (position_min != position_max - 1) {
  243. // Calculate middle between min and max
  244. uint16_t mid = position_min + ((position_max - position_min) >> 1);
  245. // Set max or min to current position
  246. if (vpage[mid] == (uint32_t)~0) {
  247. position_max = mid;
  248. } else {
  249. position_min = mid;
  250. }
  251. }
  252. return position_max;
  253. }
  254. uint8_t NVRAMClass::get_byte_from_page(uint32_t *vpage, uint16_t log_start,
  255. uint16_t log_end, uint16_t idx)
  256. {
  257. // mask matching a bit signaling wich address range is in log
  258. uint32_t address_mask = ADDR2BIT(idx);
  259. // mask matching the index address
  260. uint32_t address_match = idx << NVRAM_ADDR_POS;
  261. // Check the log backwards
  262. while (log_end > log_start) {
  263. log_end--;
  264. uint32_t value = vpage[log_end];
  265. // end here if address map bit is not set
  266. if ((value & address_mask) == 0) {
  267. break;
  268. }
  269. // check address match -> update found -> return
  270. if ((value & NVRAM_ADDR_MASK) == address_match) {
  271. return (uint8_t)value;
  272. }
  273. }
  274. // Calculate address in the eeprom map at the beginning of a vpage
  275. uint16_t map_address = (idx >> 2);
  276. map_address++; // jump over log offset field
  277. // look at map if calculated addess before log start position
  278. if (map_address < log_start) {
  279. switch (idx % 4) {
  280. case 3:
  281. return (uint8_t)(vpage[map_address] >> 24);
  282. break;
  283. case 2:
  284. return (uint8_t)(vpage[map_address] >> 16);
  285. break;
  286. case 1:
  287. return (uint8_t)(vpage[map_address] >> 8);
  288. break;
  289. default:
  290. return (uint8_t)(vpage[map_address]);
  291. break;
  292. }
  293. }
  294. // empty cell
  295. return 0xff;
  296. }