summaryrefslogtreecommitdiff
path: root/src/storage.h
blob: 6b761901632e21cc3a1d30cf4bd67f2d7fdf2e08 (plain)
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/*
 * Copyright (C) 2016 by Daniel Friesel
 *
 * License: You may use, redistribute and/or modify this file under the terms
 * of either:
 * * The GNU LGPL v3 (see COPYING and COPYING.LESSER), or
 * * The 3-clause BSD License (see COPYING.BSD)
 *
 */

#include <stdlib.h>

#define I2C_EEPROM_ADDR 0x50

class Storage {
	private:
		/**
		 * Number of animations on the storage, AKA contents of byte 0x0000.
		 * A value of 0xff indicates that the EEPROM was never written to
		 * and therefore contains no animations.
		 */
		uint8_t num_anims;

		/**
		 * Page offset of the pattern read by the last load() call. Used to
		 * calculate the read address in loadChunk(). The animation this
		 * offset refers to starts at byte 256 + (32 * page_offset).
		 */
		uint8_t page_offset;

		/**
		 * First free page (excluding the 256 byte metadata area) on the
		 * EEPROM. Used by save() and append(). This value refers to the
		 * EEPROM bytes 256 + (32 * first_free_page) and up.
		 */
		uint8_t first_free_page;

		enum I2CStatus : uint8_t {
			I2C_OK,
			I2C_START_ERR,
			I2C_ADDR_ERR,
			I2C_ERR
		};

		/**
		 * Sends an I2C start condition and the I2C_EEPROM_ADDR with the
		 * write flag set.
		 *
		 * @return An I2CStatus value indicating success/failure
		 */
		uint8_t i2c_start_write(void);

		/**
		 * Sends an I2C start condition and the I2C_EEPROM_ADDR with the
		 * read flag set.
		 *
		 * @return An I2CStatus value indicating success/failure
		 */
		uint8_t i2c_start_read(void);

		/**
		 * Sends an I2C stop condition. Does not check for errors.
		 */
		void i2c_stop(void);

		/**
		 * Sends up to len bytes of data via I2C. i2c_start_write() must
		 * have been called successfully for this to work.
		 *
		 * @param len number of bytes to send
		 * @param data pointer to data buffer, must be at least len bytes
		 * @return number of bytes which were successfully sent (0 .. len)
		 */
		uint8_t i2c_send(uint8_t len, uint8_t *data);

		/**
		 * Receives up to len bytes of data via I2C. i2c_start_read() must
		 * have been called successfully for this to work.
		 *
		 * @param len number of bytes to receive
		 * @param data pointer to data buffer, must be at least len bytes
		 * @return number of bytes which were successfully received (0 .. len)
		 */
		uint8_t i2c_receive(uint8_t len, uint8_t *data);

		/**
		 * Reads len bytes of data stored on addrhi, addrlo from the EEPROM
		 * into the data buffer. Does a complete I2C transaction including
		 * start and stop conditions. addrlo may start at an arbitrary
		 * position, page boundaries are irrelevant for this function. If a
		 * read reaches the end of the EEPROM memory, it will wrap around to
		 * byte 0x0000.
		 *
		 * @param addrhi upper address byte. Must be less than 32
		 * @param addrlo lower address byte
		 * @param len number of bytes to read
		 * @param data pointer to data buffer, must be at least len bytes
		 * @return An I2CStatus value indicating success/failure
		 */
		uint8_t i2c_read(uint8_t addrhi, uint8_t addrlo, uint8_t len, uint8_t *data);

		/**
		 * Writes len bytes of data from the data buffer into addrhi, addrlo
		 * on the EEPROM. Does a complete I2C transaction including start and
		 * stop conditions. Note that the EEPROM consists of 32 byte pages,
		 * so it is not possible to write more than 32 bytes in one
		 * operation.  Also note that this function does not check for page
		 * boundaries.  Starting a 32-byte write in the middle of a page will
		 * put the first 16 bytes where they belong, while the second 16
		 * bytes will wrap around to the first 16 bytes of the page.
		 *
		 * @param addrhi upper address byte. Must be less than 32
		 * @param addrlo lower address byte
		 * @param len number of bytes to write
		 * @param data pointer to data buffer, must be at least len bytes
		 * @return An I2CStatus value indicating success/failure
		 */
		uint8_t i2c_write(uint8_t addrhi, uint8_t addrlo, uint8_t len, uint8_t *data);

	public:
		Storage() { num_anims = 0; first_free_page = 0;};

		/**
		 * Enables the storage hardware: Configures the internal I2C
		 * module and reads num_anims from the EEPROM.
		 */
		void enable();

		/**
		 * Prepares the storage for a complete overwrite by setting the
		 * number of stored animations to zero. The next save operation
		 * will get pattern id 0 and overwrite the first stored pattern.
		 *
		 * Note that this function does not write anything to the
		 * EEPROM. Use Storage::sync() for that.
		 */
		void reset();

		/**
		 * Writes the current number of animations (as set by reset() or
		 * save() to the EEPROM. Required to get a consistent storage state
		 * after a power cycle.
		 */
		void sync();

		/**
		 * Checks whether the EEPROM contains animathion data.
		 *
		 * @return true if the EEPROM contains valid-looking data
		 */
		bool hasData();

		/**
		 * Accessor for the number of saved patterns on the EEPROM.
		 * A return value of 255 (0xff) means that there are no patterns
		 * on the EEPROM (and hasData() returns false in that case).
		 *
		 * @return number of patterns
		 */
		uint8_t numPatterns() { return num_anims; };

		/**
		 * Loads pattern number idx from the EEPROM. The 
		 *
		 * @param idx pattern index (starting with 0)
		 * @param pointer to the data structure for the pattern. Must be
		 *        at least 132 bytes
		 */
		void load(uint8_t idx, uint8_t *data);

		/**
		 * Load partial pattern chunk (without header) from EEPROM.
		 *
		 * @param chunk 128 byte-offset inside pattern (starting with 0)
		 * @param data pointer to data structure for the pattern. Must be
		 *        at least 128 bytes
		 */
		void loadChunk(uint8_t chunk, uint8_t *data);

		/**
		 * Save (possibly partial) pattern on the EEPROM. 32 bytes of
		 * dattern data will be read and stored, regardless of the
		 * pattern header.
		 *
		 * @param data pattern data. Must be at least 32 bytes
		 */
		void save(uint8_t *data);

		/**
		 * Continue saving a pattern on the EEPROM. Appends 32 bytes of
		 * pattern data after the most recently written block of data
		 * (i.e., to the pattern which is currently being saved).
		 *
		 * @param data pattern data. Must be at least 32 bytes
		 */
		void append(uint8_t *data);
};

extern Storage storage;