paul@3 | 1 | // Copyright 2013 Pervasive Displays, Inc. |
paul@3 | 2 | // |
paul@3 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
paul@3 | 4 | // you may not use this file except in compliance with the License. |
paul@3 | 5 | // You may obtain a copy of the License at: |
paul@3 | 6 | // |
paul@3 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
paul@3 | 8 | // |
paul@3 | 9 | // Unless required by applicable law or agreed to in writing, |
paul@3 | 10 | // software distributed under the License is distributed on an |
paul@3 | 11 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either |
paul@3 | 12 | // express or implied. See the License for the specific language |
paul@3 | 13 | // governing permissions and limitations under the License. |
paul@3 | 14 | |
paul@3 | 15 | |
paul@3 | 16 | #include "EPD.h" |
paul@3 | 17 | |
paul@3 | 18 | typedef enum { |
paul@3 | 19 | LOW=0, |
paul@3 | 20 | HIGH=1 |
paul@3 | 21 | } EPD_pinstate; |
paul@3 | 22 | |
paul@3 | 23 | static void EPD_line(uint16_t line, const uint8_t *data, uint8_t fixed_value, uint8_t read_progmem, EPD_stage stage); |
paul@3 | 24 | static void SPI_send(const uint8_t *buffer, uint16_t length); |
paul@3 | 25 | static void SPI_put(uint8_t c); |
paul@3 | 26 | static void SPI_put_wait(uint8_t c); |
paul@3 | 27 | |
paul@3 | 28 | static void EPD_Pin_init(); |
paul@3 | 29 | static void EPD_Pin_EPD_CS(EPD_pinstate pin); |
paul@3 | 30 | static void EPD_Pin_RESET(EPD_pinstate pin); |
paul@3 | 31 | static void EPD_Pin_PANEL_ON(EPD_pinstate pin); |
paul@3 | 32 | static void EPD_Pin_DISCHARGE(EPD_pinstate pin); |
paul@3 | 33 | static void EPD_Pin_BORDER(EPD_pinstate pin); |
paul@3 | 34 | static EPD_pinstate epd_get_busy(void); |
paul@3 | 35 | |
paul@3 | 36 | static void epd_pwm_active(uint16_t delayInMs); |
paul@3 | 37 | |
paul@3 | 38 | |
paul@3 | 39 | // inline arrays |
paul@3 | 40 | #define ARRAY(type, ...) ((type[]){__VA_ARGS__}) |
paul@3 | 41 | #define CU8(...) (ARRAY(const uint8_t, __VA_ARGS__)) |
paul@3 | 42 | |
paul@3 | 43 | static COG_Parameters_t epd; |
paul@3 | 44 | |
paul@3 | 45 | /****************************************************************************** |
paul@3 | 46 | * Local functions |
paul@3 | 47 | *****************************************************************************/ |
paul@3 | 48 | |
paul@3 | 49 | |
paul@3 | 50 | // convert a temperature in Celcius to |
paul@3 | 51 | // the scale factor for frame_*_repeat methods |
paul@3 | 52 | static int16_t EPD_temperature_to_factor_10x(int16_t temperature) { |
paul@3 | 53 | if (temperature <= -10) { |
paul@3 | 54 | return 170; |
paul@3 | 55 | } else if (temperature <= -5) { |
paul@3 | 56 | return 120; |
paul@3 | 57 | } else if (temperature <= 5) { |
paul@3 | 58 | return 80; |
paul@3 | 59 | } else if (temperature <= 10) { |
paul@3 | 60 | return 40; |
paul@3 | 61 | } else if (temperature <= 15) { |
paul@3 | 62 | return 30; |
paul@3 | 63 | } else if (temperature <= 20) { |
paul@3 | 64 | return 20; |
paul@3 | 65 | } else if (temperature <= 40) { |
paul@3 | 66 | return 10; |
paul@3 | 67 | } |
paul@3 | 68 | return 7; |
paul@3 | 69 | } |
paul@3 | 70 | |
paul@3 | 71 | |
paul@3 | 72 | // One frame of data is the number of lines * rows. For example: |
paul@3 | 73 | // The 1.44??? frame of data is 96 lines * 128 dots. |
paul@3 | 74 | // The 2??? frame of data is 96 lines * 200 dots. |
paul@3 | 75 | // The 2.7??? frame of data is 176 lines * 264 dots. |
paul@3 | 76 | |
paul@3 | 77 | // the image is arranged by line which matches the display size |
paul@3 | 78 | // so smallest would have 96 * 32 bytes |
paul@3 | 79 | |
paul@3 | 80 | static void EPD_frame_fixed(uint8_t fixed_value, EPD_stage stage) { |
paul@3 | 81 | uint8_t line; |
paul@3 | 82 | for (line = 0; line < epd.lines_per_display ; ++line) { |
paul@3 | 83 | EPD_line(line, 0, fixed_value, FALSE, stage); |
paul@3 | 84 | } |
paul@3 | 85 | } |
paul@3 | 86 | |
paul@3 | 87 | static void EPD_frame_data(PROGMEM const uint8_t *image, EPD_stage stage){ |
paul@3 | 88 | uint8_t line; |
paul@3 | 89 | for (line = 0; line < epd.lines_per_display ; ++line) { |
paul@3 | 90 | EPD_line(line, &image[line * epd.bytes_per_line], 0, TRUE, stage); |
paul@3 | 91 | } |
paul@3 | 92 | } |
paul@3 | 93 | |
paul@3 | 94 | |
paul@3 | 95 | #if defined(EPD_ENABLE_EXTRA_SRAM) |
paul@3 | 96 | static void EPD_frame_sram(const uint8_t *image, EPD_stage stage){ |
paul@3 | 97 | uint8_t line; |
paul@3 | 98 | for (line = 0; line < epd.lines_per_display ; ++line) { |
paul@3 | 99 | EPD_line(line, &image[line * epd.bytes_per_line], 0, FALSE, stage); |
paul@3 | 100 | } |
paul@3 | 101 | } |
paul@3 | 102 | #endif |
paul@3 | 103 | |
paul@3 | 104 | |
paul@3 | 105 | static void EPD_frame_fixed_repeat(uint8_t fixed_value, EPD_stage stage) { |
paul@3 | 106 | int32_t stage_time = epd.factored_stage_time; |
paul@3 | 107 | do { |
paul@3 | 108 | uint32_t t_start = bsp_getMsTicks(); |
paul@3 | 109 | EPD_frame_fixed(fixed_value, stage); |
paul@3 | 110 | uint32_t t_end = bsp_getMsTicks(); |
paul@3 | 111 | if (t_end > t_start) { |
paul@3 | 112 | stage_time -= t_end - t_start; |
paul@3 | 113 | } else { |
paul@3 | 114 | stage_time -= t_start - t_end + 1 + 0xffffffffU; |
paul@3 | 115 | } |
paul@3 | 116 | } while (stage_time > 0); |
paul@3 | 117 | } |
paul@3 | 118 | |
paul@3 | 119 | |
paul@3 | 120 | static void EPD_frame_data_repeat(PROGMEM const uint8_t *image, EPD_stage stage) { |
paul@3 | 121 | int32_t stage_time = epd.factored_stage_time; |
paul@3 | 122 | do { |
paul@3 | 123 | uint32_t t_start = bsp_getMsTicks(); |
paul@3 | 124 | EPD_frame_data(image, stage); |
paul@3 | 125 | uint32_t t_end = bsp_getMsTicks(); |
paul@3 | 126 | if (t_end > t_start) { |
paul@3 | 127 | stage_time -= t_end - t_start; |
paul@3 | 128 | } else { |
paul@3 | 129 | stage_time -= t_start - t_end + 1 + 0xffffffffU; |
paul@3 | 130 | } |
paul@3 | 131 | } while (stage_time > 0); |
paul@3 | 132 | } |
paul@3 | 133 | |
paul@3 | 134 | #include <limits.h> |
paul@3 | 135 | #if defined(EPD_ENABLE_EXTRA_SRAM) |
paul@3 | 136 | static void EPD_frame_sram_repeat(const uint8_t *image, EPD_stage stage) { |
paul@3 | 137 | int32_t stage_time = epd.factored_stage_time; |
paul@3 | 138 | do { |
paul@3 | 139 | uint32_t t_start = bsp_getMsTicks(); |
paul@3 | 140 | EPD_frame_sram(image, stage); |
paul@3 | 141 | uint32_t t_end = bsp_getMsTicks(); |
paul@3 | 142 | if (t_end > t_start) { |
paul@3 | 143 | stage_time -= t_end - t_start; |
paul@3 | 144 | } else { |
paul@3 | 145 | stage_time -= t_start - t_end + 1 + 0xffffffffU; |
paul@3 | 146 | } |
paul@3 | 147 | } while (stage_time > 0); |
paul@3 | 148 | } |
paul@3 | 149 | #endif |
paul@3 | 150 | |
paul@3 | 151 | #if 0 |
paul@3 | 152 | typedef void EPD_reader(void *buffer, uint32_t address, uint16_t length); |
paul@3 | 153 | |
paul@3 | 154 | static void EPD_frame_cb(uint32_t address, EPD_reader *reader, EPD_stage stage) { |
paul@3 | 155 | static uint8_t buffer[264 / 8]; |
paul@3 | 156 | uint8_t line; |
paul@3 | 157 | for (line = 0; line < epd.lines_per_display; ++line) { |
paul@3 | 158 | reader(buffer, address + line * epd.bytes_per_line, epd.bytes_per_line); |
paul@3 | 159 | EPD_line(line, buffer, 0, FALSE, stage); |
paul@3 | 160 | } |
paul@3 | 161 | } |
paul@3 | 162 | |
paul@3 | 163 | static void EPD_frame_cb_repeat(uint32_t address, EPD_reader *reader, EPD_stage stage) { |
paul@3 | 164 | int32_t stage_time = epd.factored_stage_time; |
paul@3 | 165 | do { |
paul@3 | 166 | uint32_t t_start = bsp_getMsTicks(); |
paul@3 | 167 | EPD_frame_cb(address, reader, stage); |
paul@3 | 168 | uint32_t t_end = bsp_getMsTicks(); |
paul@3 | 169 | if (t_end > t_start) { |
paul@3 | 170 | stage_time -= t_end - t_start; |
paul@3 | 171 | } else { |
paul@3 | 172 | stage_time -= t_start - t_end + 1 + 0xffffffffU; |
paul@3 | 173 | } |
paul@3 | 174 | } while (stage_time > 0); |
paul@3 | 175 | } |
paul@3 | 176 | #endif |
paul@3 | 177 | |
paul@3 | 178 | static void EPD_line(uint16_t line, const uint8_t *data, uint8_t fixed_value, uint8_t read_progmem, EPD_stage stage) { |
paul@3 | 179 | // charge pump voltage levels |
paul@3 | 180 | bsp_delayUs(10); |
paul@3 | 181 | SPI_send(CU8(0x70, 0x04), 2); |
paul@3 | 182 | bsp_delayUs(10); |
paul@3 | 183 | SPI_send(epd.gate_source, epd.gate_source_length); |
paul@3 | 184 | |
paul@3 | 185 | // send data |
paul@3 | 186 | bsp_delayUs(10); |
paul@3 | 187 | SPI_send(CU8(0x70, 0x0a), 2); |
paul@3 | 188 | bsp_delayUs(10); |
paul@3 | 189 | |
paul@3 | 190 | // CS low |
paul@3 | 191 | EPD_Pin_EPD_CS(LOW); |
paul@3 | 192 | SPI_put_wait(0x72); |
paul@3 | 193 | |
paul@3 | 194 | // even pixels |
paul@3 | 195 | uint16_t b; |
paul@3 | 196 | for (b = epd.bytes_per_line; b > 0; --b) { |
paul@3 | 197 | if (0 != data) { |
paul@3 | 198 | uint8_t pixels = data[b - 1] & 0xaa; |
paul@3 | 199 | switch(stage) { |
paul@3 | 200 | case EPD_compensate: // B -> W, W -> B (Current Image) |
paul@3 | 201 | pixels = 0xaa | ((pixels ^ 0xaa) >> 1); |
paul@3 | 202 | break; |
paul@3 | 203 | case EPD_white: // B -> N, W -> W (Current Image) |
paul@3 | 204 | pixels = 0x55 + ((pixels ^ 0xaa) >> 1); |
paul@3 | 205 | break; |
paul@3 | 206 | case EPD_inverse: // B -> N, W -> B (New Image) |
paul@3 | 207 | pixels = 0x55 | (pixels ^ 0xaa); |
paul@3 | 208 | break; |
paul@3 | 209 | case EPD_normal: // B -> B, W -> W (New Image) |
paul@3 | 210 | pixels = 0xaa | (pixels >> 1); |
paul@3 | 211 | break; |
paul@3 | 212 | } |
paul@3 | 213 | SPI_put_wait(pixels); |
paul@3 | 214 | } else { |
paul@3 | 215 | SPI_put_wait(fixed_value); |
paul@3 | 216 | } } |
paul@3 | 217 | |
paul@3 | 218 | // scan line |
paul@3 | 219 | for (b = 0; b < epd.bytes_per_scan; ++b) { |
paul@3 | 220 | if (line / 4 == b) { |
paul@3 | 221 | SPI_put_wait(0xc0 >> (2 * (line & 0x03))); |
paul@3 | 222 | } else { |
paul@3 | 223 | SPI_put_wait(0x00); |
paul@3 | 224 | } |
paul@3 | 225 | } |
paul@3 | 226 | |
paul@3 | 227 | // odd pixels |
paul@3 | 228 | for (b = 0; b < epd.bytes_per_line; ++b) { |
paul@3 | 229 | if (0 != data) { |
paul@3 | 230 | uint8_t pixels = data[b] & 0x55; |
paul@3 | 231 | switch(stage) { |
paul@3 | 232 | case EPD_compensate: // B -> W, W -> B (Current Image) |
paul@3 | 233 | pixels = 0xaa | (pixels ^ 0x55); |
paul@3 | 234 | break; |
paul@3 | 235 | case EPD_white: // B -> N, W -> W (Current Image) |
paul@3 | 236 | pixels = 0x55 + (pixels ^ 0x55); |
paul@3 | 237 | break; |
paul@3 | 238 | case EPD_inverse: // B -> N, W -> B (New Image) |
paul@3 | 239 | pixels = 0x55 | ((pixels ^ 0x55) << 1); |
paul@3 | 240 | break; |
paul@3 | 241 | case EPD_normal: // B -> B, W -> W (New Image) |
paul@3 | 242 | pixels = 0xaa | pixels; |
paul@3 | 243 | break; |
paul@3 | 244 | } |
paul@3 | 245 | uint8_t p1 = (pixels >> 6) & 0x03; |
paul@3 | 246 | uint8_t p2 = (pixels >> 4) & 0x03; |
paul@3 | 247 | uint8_t p3 = (pixels >> 2) & 0x03; |
paul@3 | 248 | uint8_t p4 = (pixels >> 0) & 0x03; |
paul@3 | 249 | pixels = (p1 << 0) | (p2 << 2) | (p3 << 4) | (p4 << 6); |
paul@3 | 250 | SPI_put_wait(pixels); |
paul@3 | 251 | } else { |
paul@3 | 252 | SPI_put_wait(fixed_value); |
paul@3 | 253 | } |
paul@3 | 254 | } |
paul@3 | 255 | |
paul@3 | 256 | if (epd.filler == TRUE) { |
paul@3 | 257 | SPI_put_wait(0x00); |
paul@3 | 258 | } |
paul@3 | 259 | |
paul@3 | 260 | // CS high |
paul@3 | 261 | EPD_Pin_EPD_CS(HIGH); |
paul@3 | 262 | |
paul@3 | 263 | // output data to panel |
paul@3 | 264 | bsp_delayUs(10); |
paul@3 | 265 | SPI_send(CU8(0x70, 0x02), 2); |
paul@3 | 266 | bsp_delayUs(10); |
paul@3 | 267 | SPI_send(CU8(0x72, 0x2f), 2); |
paul@3 | 268 | } |
paul@3 | 269 | |
paul@3 | 270 | |
paul@3 | 271 | /****************************************************************************** |
paul@3 | 272 | * Public functions |
paul@3 | 273 | *****************************************************************************/ |
paul@3 | 274 | |
paul@3 | 275 | |
paul@3 | 276 | void EPD_init(EPD_size size) { |
paul@3 | 277 | static uint8_t cs[] = {0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00}; |
paul@3 | 278 | static uint8_t gs[] = {0x72, 0x03}; |
paul@3 | 279 | |
paul@3 | 280 | EPD_Pin_init(); |
paul@3 | 281 | |
paul@3 | 282 | epd.size = size; |
paul@3 | 283 | epd.stage_time = 480; // milliseconds |
paul@3 | 284 | epd.lines_per_display = 96; |
paul@3 | 285 | epd.dots_per_line = 128; |
paul@3 | 286 | epd.bytes_per_line = 128 / 8; |
paul@3 | 287 | epd.bytes_per_scan = 96 / 4; |
paul@3 | 288 | epd.filler = FALSE; |
paul@3 | 289 | epd.channel_select = cs; |
paul@3 | 290 | epd.channel_select_length = sizeof(cs); |
paul@3 | 291 | epd.gate_source = gs; |
paul@3 | 292 | epd.gate_source_length = sizeof(gs); |
paul@3 | 293 | |
paul@3 | 294 | // set up size structure |
paul@3 | 295 | switch (size) { |
paul@3 | 296 | default: |
paul@3 | 297 | case EPD_1_44: // default so no change |
paul@3 | 298 | break; |
paul@3 | 299 | |
paul@3 | 300 | case EPD_2_0: { |
paul@3 | 301 | epd.lines_per_display = 96; |
paul@3 | 302 | epd.dots_per_line = 200; |
paul@3 | 303 | epd.bytes_per_line = 200 / 8; |
paul@3 | 304 | epd.bytes_per_scan = 96 / 4; |
paul@3 | 305 | epd.filler = TRUE; |
paul@3 | 306 | cs[0] = 0x72; |
paul@3 | 307 | cs[1] = 0x00; |
paul@3 | 308 | cs[2] = 0x00; |
paul@3 | 309 | cs[3] = 0x00; |
paul@3 | 310 | cs[4] = 0x00; |
paul@3 | 311 | cs[5] = 0x01; |
paul@3 | 312 | cs[6] = 0xff; |
paul@3 | 313 | cs[7] = 0xe0; |
paul@3 | 314 | cs[8] = 0x00; |
paul@3 | 315 | gs[0] = 0x72; |
paul@3 | 316 | gs[1] = 0x03; |
paul@3 | 317 | break; |
paul@3 | 318 | } |
paul@3 | 319 | |
paul@3 | 320 | case EPD_2_7: { |
paul@3 | 321 | epd.stage_time = 630; // milliseconds |
paul@3 | 322 | epd.lines_per_display = 176; |
paul@3 | 323 | epd.dots_per_line = 264; |
paul@3 | 324 | epd.bytes_per_line = 264 / 8; |
paul@3 | 325 | epd.bytes_per_scan = 176 / 4; |
paul@3 | 326 | epd.filler = TRUE; |
paul@3 | 327 | cs[0] = 0x72; |
paul@3 | 328 | cs[1] = 0x00; |
paul@3 | 329 | cs[2] = 0x00; |
paul@3 | 330 | cs[3] = 0x00; |
paul@3 | 331 | cs[4] = 0x7f; |
paul@3 | 332 | cs[5] = 0xff; |
paul@3 | 333 | cs[6] = 0xfe; |
paul@3 | 334 | cs[7] = 0x00; |
paul@3 | 335 | cs[8] = 0x00; |
paul@3 | 336 | gs[0] = 0x72; |
paul@3 | 337 | gs[1] = 0x00; |
paul@3 | 338 | break; |
paul@3 | 339 | } |
paul@3 | 340 | } |
paul@3 | 341 | |
paul@3 | 342 | epd.factored_stage_time = epd.stage_time; |
paul@3 | 343 | } |
paul@3 | 344 | |
paul@3 | 345 | |
paul@3 | 346 | void EPD_begin(void) |
paul@3 | 347 | { |
paul@3 | 348 | // power up sequence |
paul@3 | 349 | SPI_put(0x00); |
paul@3 | 350 | |
paul@3 | 351 | EPD_Pin_RESET(LOW); |
paul@3 | 352 | EPD_Pin_PANEL_ON(LOW); |
paul@3 | 353 | EPD_Pin_DISCHARGE(LOW); |
paul@3 | 354 | EPD_Pin_BORDER(LOW); |
paul@3 | 355 | EPD_Pin_EPD_CS(LOW); |
paul@3 | 356 | |
paul@3 | 357 | // PWM_start(this->EPD_Pin_PWM); |
paul@3 | 358 | // Delay_ms(5); |
paul@3 | 359 | epd_pwm_active(5); |
paul@3 | 360 | EPD_Pin_PANEL_ON(HIGH); |
paul@3 | 361 | // Delay_ms(10); |
paul@3 | 362 | epd_pwm_active(10); |
paul@3 | 363 | |
paul@3 | 364 | EPD_Pin_RESET(HIGH); |
paul@3 | 365 | EPD_Pin_BORDER(HIGH); |
paul@3 | 366 | EPD_Pin_EPD_CS(HIGH); |
paul@3 | 367 | // Delay_ms(5); |
paul@3 | 368 | epd_pwm_active(5); |
paul@3 | 369 | |
paul@3 | 370 | EPD_Pin_RESET(LOW); |
paul@3 | 371 | // Delay_ms(5); |
paul@3 | 372 | epd_pwm_active(5); |
paul@3 | 373 | |
paul@3 | 374 | EPD_Pin_RESET(HIGH); |
paul@3 | 375 | // Delay_ms(5); |
paul@3 | 376 | epd_pwm_active(5); |
paul@3 | 377 | |
paul@3 | 378 | // wait for COG to become ready |
paul@3 | 379 | while (HIGH == epd_get_busy()) { |
paul@3 | 380 | epd_pwm_active(10); |
paul@3 | 381 | } |
paul@3 | 382 | |
paul@3 | 383 | // channel select |
paul@3 | 384 | bsp_delayUs(10); |
paul@3 | 385 | SPI_send(CU8(0x70, 0x01), 2); |
paul@3 | 386 | bsp_delayUs(10); |
paul@3 | 387 | SPI_send(epd.channel_select, epd.channel_select_length); |
paul@3 | 388 | |
paul@3 | 389 | // DC/DC frequency |
paul@3 | 390 | bsp_delayUs(10); |
paul@3 | 391 | SPI_send(CU8(0x70, 0x06), 2); |
paul@3 | 392 | bsp_delayUs(10); |
paul@3 | 393 | SPI_send(CU8(0x72, 0xff), 2); |
paul@3 | 394 | |
paul@3 | 395 | // high power mode osc |
paul@3 | 396 | bsp_delayUs(10); |
paul@3 | 397 | SPI_send(CU8(0x70, 0x07), 2); |
paul@3 | 398 | bsp_delayUs(10); |
paul@3 | 399 | SPI_send(CU8(0x72, 0x9d), 2); |
paul@3 | 400 | |
paul@3 | 401 | |
paul@3 | 402 | // disable ADC |
paul@3 | 403 | bsp_delayUs(10); |
paul@3 | 404 | SPI_send(CU8(0x70, 0x08), 2); |
paul@3 | 405 | bsp_delayUs(10); |
paul@3 | 406 | SPI_send(CU8(0x72, 0x00), 2); |
paul@3 | 407 | |
paul@3 | 408 | // Vcom level |
paul@3 | 409 | bsp_delayUs(10); |
paul@3 | 410 | SPI_send(CU8(0x70, 0x09), 2); |
paul@3 | 411 | bsp_delayUs(10); |
paul@3 | 412 | SPI_send(CU8(0x72, 0xd0, 0x00), 3); |
paul@3 | 413 | |
paul@3 | 414 | // gate and source voltage levels |
paul@3 | 415 | bsp_delayUs(10); |
paul@3 | 416 | SPI_send(CU8(0x70, 0x04), 2); |
paul@3 | 417 | bsp_delayUs(10); |
paul@3 | 418 | SPI_send(epd.gate_source, epd.gate_source_length); |
paul@3 | 419 | |
paul@3 | 420 | // Delay_ms(5); //??? |
paul@3 | 421 | epd_pwm_active(5); |
paul@3 | 422 | |
paul@3 | 423 | // driver latch on |
paul@3 | 424 | bsp_delayUs(10); |
paul@3 | 425 | SPI_send(CU8(0x70, 0x03), 2); |
paul@3 | 426 | bsp_delayUs(10); |
paul@3 | 427 | SPI_send(CU8(0x72, 0x01), 2); |
paul@3 | 428 | |
paul@3 | 429 | // driver latch off |
paul@3 | 430 | bsp_delayUs(10); |
paul@3 | 431 | SPI_send(CU8(0x70, 0x03), 2); |
paul@3 | 432 | bsp_delayUs(10); |
paul@3 | 433 | SPI_send(CU8(0x72, 0x00), 2); |
paul@3 | 434 | |
paul@3 | 435 | // Delay_ms(5); |
paul@3 | 436 | epd_pwm_active(5); |
paul@3 | 437 | |
paul@3 | 438 | // charge pump positive voltage on |
paul@3 | 439 | bsp_delayUs(10); |
paul@3 | 440 | SPI_send(CU8(0x70, 0x05), 2); |
paul@3 | 441 | bsp_delayUs(10); |
paul@3 | 442 | SPI_send(CU8(0x72, 0x01), 2); |
paul@3 | 443 | |
paul@3 | 444 | // final delay before PWM off |
paul@3 | 445 | // Delay_ms(30); |
paul@3 | 446 | // PWM_stop(this->EPD_Pin_PWM); |
paul@3 | 447 | epd_pwm_active(30); |
paul@3 | 448 | |
paul@3 | 449 | // charge pump negative voltage on |
paul@3 | 450 | bsp_delayUs(10); |
paul@3 | 451 | SPI_send(CU8(0x70, 0x05), 2); |
paul@3 | 452 | bsp_delayUs(10); |
paul@3 | 453 | SPI_send(CU8(0x72, 0x03), 2); |
paul@3 | 454 | |
paul@3 | 455 | bsp_delayMs(30); |
paul@3 | 456 | |
paul@3 | 457 | // Vcom driver on |
paul@3 | 458 | bsp_delayUs(10); |
paul@3 | 459 | SPI_send(CU8(0x70, 0x05), 2); |
paul@3 | 460 | bsp_delayUs(10); |
paul@3 | 461 | SPI_send(CU8(0x72, 0x0f), 2); |
paul@3 | 462 | |
paul@3 | 463 | bsp_delayMs(30); |
paul@3 | 464 | |
paul@3 | 465 | // output enable to disable |
paul@3 | 466 | bsp_delayUs(10); |
paul@3 | 467 | SPI_send(CU8(0x70, 0x02), 2); |
paul@3 | 468 | bsp_delayUs(10); |
paul@3 | 469 | SPI_send(CU8(0x72, 0x24), 2); |
paul@3 | 470 | } |
paul@3 | 471 | |
paul@3 | 472 | |
paul@3 | 473 | void EPD_end(void) { |
paul@3 | 474 | |
paul@3 | 475 | EPD_frame_fixed(0x55, EPD_normal); // dummy frame |
paul@3 | 476 | EPD_line(0x7fffu, 0, 0x55, FALSE, EPD_normal); // dummy_line |
paul@3 | 477 | |
paul@3 | 478 | bsp_delayMs(25); |
paul@3 | 479 | |
paul@3 | 480 | EPD_Pin_BORDER(LOW); |
paul@3 | 481 | bsp_delayMs(30); |
paul@3 | 482 | |
paul@3 | 483 | EPD_Pin_BORDER(HIGH); |
paul@3 | 484 | |
paul@3 | 485 | // latch reset turn on |
paul@3 | 486 | bsp_delayUs(10); |
paul@3 | 487 | SPI_send(CU8(0x70, 0x03), 2); |
paul@3 | 488 | bsp_delayUs(10); |
paul@3 | 489 | SPI_send(CU8(0x72, 0x01), 2); |
paul@3 | 490 | |
paul@3 | 491 | // output enable off |
paul@3 | 492 | bsp_delayUs(10); |
paul@3 | 493 | SPI_send(CU8(0x70, 0x02), 2); |
paul@3 | 494 | bsp_delayUs(10); |
paul@3 | 495 | SPI_send(CU8(0x72, 0x05), 2); |
paul@3 | 496 | |
paul@3 | 497 | // Vcom power off |
paul@3 | 498 | bsp_delayUs(10); |
paul@3 | 499 | SPI_send(CU8(0x70, 0x05), 2); |
paul@3 | 500 | bsp_delayUs(10); |
paul@3 | 501 | SPI_send(CU8(0x72, 0x0e), 2); |
paul@3 | 502 | |
paul@3 | 503 | // power off negative charge pump |
paul@3 | 504 | bsp_delayUs(10); |
paul@3 | 505 | SPI_send(CU8(0x70, 0x05), 2); |
paul@3 | 506 | bsp_delayUs(10); |
paul@3 | 507 | SPI_send(CU8(0x72, 0x02), 2); |
paul@3 | 508 | |
paul@3 | 509 | // discharge |
paul@3 | 510 | bsp_delayUs(10); |
paul@3 | 511 | SPI_send(CU8(0x70, 0x04), 2); |
paul@3 | 512 | bsp_delayUs(10); |
paul@3 | 513 | SPI_send(CU8(0x72, 0x0c), 2); |
paul@3 | 514 | |
paul@3 | 515 | bsp_delayMs(120); |
paul@3 | 516 | |
paul@3 | 517 | // all charge pumps off |
paul@3 | 518 | bsp_delayUs(10); |
paul@3 | 519 | SPI_send(CU8(0x70, 0x05), 2); |
paul@3 | 520 | bsp_delayUs(10); |
paul@3 | 521 | SPI_send(CU8(0x72, 0x00), 2); |
paul@3 | 522 | |
paul@3 | 523 | // turn of osc |
paul@3 | 524 | bsp_delayUs(10); |
paul@3 | 525 | SPI_send(CU8(0x70, 0x07), 2); |
paul@3 | 526 | bsp_delayUs(10); |
paul@3 | 527 | SPI_send(CU8(0x72, 0x0d), 2); |
paul@3 | 528 | |
paul@3 | 529 | // discharge internal - 1 |
paul@3 | 530 | bsp_delayUs(10); |
paul@3 | 531 | SPI_send(CU8(0x70, 0x04), 2); |
paul@3 | 532 | bsp_delayUs(10); |
paul@3 | 533 | SPI_send(CU8(0x72, 0x50), 2); |
paul@3 | 534 | |
paul@3 | 535 | bsp_delayMs(40); |
paul@3 | 536 | |
paul@3 | 537 | // discharge internal - 2 |
paul@3 | 538 | bsp_delayUs(10); |
paul@3 | 539 | SPI_send(CU8(0x70, 0x04), 2); |
paul@3 | 540 | bsp_delayUs(10); |
paul@3 | 541 | SPI_send(CU8(0x72, 0xA0), 2); |
paul@3 | 542 | |
paul@3 | 543 | bsp_delayMs(40); |
paul@3 | 544 | |
paul@3 | 545 | // discharge internal - 3 |
paul@3 | 546 | bsp_delayUs(10); |
paul@3 | 547 | SPI_send(CU8(0x70, 0x04), 2); |
paul@3 | 548 | bsp_delayUs(10); |
paul@3 | 549 | SPI_send(CU8(0x72, 0x00), 2); |
paul@3 | 550 | |
paul@3 | 551 | // turn of power and all signals |
paul@3 | 552 | EPD_Pin_RESET(LOW); |
paul@3 | 553 | EPD_Pin_PANEL_ON(LOW); |
paul@3 | 554 | EPD_Pin_BORDER(LOW); |
paul@3 | 555 | EPD_Pin_EPD_CS(LOW); |
paul@3 | 556 | |
paul@3 | 557 | EPD_Pin_DISCHARGE(HIGH); |
paul@3 | 558 | |
paul@3 | 559 | SPI_put(0x00); |
paul@3 | 560 | |
paul@3 | 561 | bsp_delayMs(150); |
paul@3 | 562 | |
paul@3 | 563 | EPD_Pin_DISCHARGE(LOW); |
paul@3 | 564 | } |
paul@3 | 565 | |
paul@3 | 566 | |
paul@3 | 567 | void EPD_setFactor(int16_t temperature) { |
paul@3 | 568 | epd.factored_stage_time = epd.stage_time * EPD_temperature_to_factor_10x(temperature) / 10; |
paul@3 | 569 | } |
paul@3 | 570 | |
paul@3 | 571 | // clear display (anything -> white) |
paul@3 | 572 | void EPD_clear() { |
paul@3 | 573 | EPD_frame_fixed_repeat(0xff, EPD_compensate); |
paul@3 | 574 | EPD_frame_fixed_repeat(0xff, EPD_white); |
paul@3 | 575 | EPD_frame_fixed_repeat(0xaa, EPD_inverse); |
paul@3 | 576 | EPD_frame_fixed_repeat(0xaa, EPD_normal); |
paul@3 | 577 | } |
paul@3 | 578 | |
paul@3 | 579 | // assuming a clear (white) screen output an image (PROGMEM data) |
paul@3 | 580 | void EPD_image(PROGMEM const uint8_t *image) { |
paul@3 | 581 | EPD_frame_fixed_repeat(0xaa, EPD_compensate); |
paul@3 | 582 | EPD_frame_fixed_repeat(0xaa, EPD_white); |
paul@3 | 583 | EPD_frame_data_repeat(image, EPD_inverse); |
paul@3 | 584 | EPD_frame_data_repeat(image, EPD_normal); |
paul@3 | 585 | } |
paul@3 | 586 | |
paul@3 | 587 | // change from old image to new image (PROGMEM data) |
paul@3 | 588 | void EPD_image_progmem(PROGMEM const uint8_t *old_image, PROGMEM const uint8_t *new_image) { |
paul@3 | 589 | EPD_frame_data_repeat(old_image, EPD_compensate); |
paul@3 | 590 | EPD_frame_data_repeat(old_image, EPD_white); |
paul@3 | 591 | EPD_frame_data_repeat(new_image, EPD_inverse); |
paul@3 | 592 | EPD_frame_data_repeat(new_image, EPD_normal); |
paul@3 | 593 | } |
paul@3 | 594 | |
paul@3 | 595 | #if defined(EPD_ENABLE_EXTRA_SRAM) |
paul@3 | 596 | // change from old image to new image (SRAM version) |
paul@3 | 597 | void EPD_image_sram(const uint8_t *old_image, const uint8_t *new_image) { |
paul@3 | 598 | EPD_frame_sram_repeat(old_image, EPD_compensate); |
paul@3 | 599 | EPD_frame_sram_repeat(old_image, EPD_white); |
paul@3 | 600 | EPD_frame_sram_repeat(new_image, EPD_inverse); |
paul@3 | 601 | EPD_frame_sram_repeat(new_image, EPD_normal); |
paul@3 | 602 | } |
paul@3 | 603 | #endif |
paul@3 | 604 | |
paul@3 | 605 | |
paul@3 | 606 | static void SPI_put(uint8_t c) { |
paul@3 | 607 | uint8_t placeholder = c; |
paul@3 | 608 | bsp_spiWrite(&placeholder, 1); |
paul@3 | 609 | } |
paul@3 | 610 | |
paul@3 | 611 | |
paul@3 | 612 | static void SPI_put_wait(uint8_t c) { |
paul@3 | 613 | |
paul@3 | 614 | SPI_put(c); |
paul@3 | 615 | |
paul@3 | 616 | // wait for COG ready |
paul@3 | 617 | while (HIGH == epd_get_busy()) { |
paul@3 | 618 | } |
paul@3 | 619 | } |
paul@3 | 620 | |
paul@3 | 621 | |
paul@3 | 622 | static void SPI_send(const uint8_t *buffer, uint16_t length) { |
paul@3 | 623 | // CS low |
paul@3 | 624 | EPD_Pin_EPD_CS(LOW); |
paul@3 | 625 | |
paul@3 | 626 | bsp_spiWrite((uint8_t*)buffer, length); |
paul@3 | 627 | |
paul@3 | 628 | // CS high |
paul@3 | 629 | EPD_Pin_EPD_CS(HIGH); |
paul@3 | 630 | } |
paul@3 | 631 | |
paul@3 | 632 | static void EPD_Pin_init() |
paul@3 | 633 | { |
paul@3 | 634 | bsp_pinDir(6, 1); // CS |
paul@3 | 635 | bsp_pinDir(7, 0); // Driver Busy |
paul@3 | 636 | bsp_pinDir(8, 1); // Border |
paul@3 | 637 | bsp_pinDir(11, 1); // Pwm |
paul@3 | 638 | bsp_pinDir(12, 1); // Reset |
paul@3 | 639 | bsp_pinDir(13, 1); // Panel On |
paul@3 | 640 | bsp_pinDir(14, 1); // Discharge |
paul@3 | 641 | } |
paul@3 | 642 | |
paul@3 | 643 | static void EPD_Pin_EPD_CS(EPD_pinstate pin) |
paul@3 | 644 | { |
paul@3 | 645 | if (HIGH == pin) { |
paul@3 | 646 | bsp_pinSet(6); |
paul@3 | 647 | } |
paul@3 | 648 | else { |
paul@3 | 649 | bsp_pinClr(6); |
paul@3 | 650 | } |
paul@3 | 651 | } |
paul@3 | 652 | |
paul@3 | 653 | static void EPD_Pin_RESET(EPD_pinstate pin) |
paul@3 | 654 | { |
paul@3 | 655 | if (HIGH == pin) { |
paul@3 | 656 | bsp_pinSet(12); |
paul@3 | 657 | } |
paul@3 | 658 | else { |
paul@3 | 659 | bsp_pinClr(12); |
paul@3 | 660 | } |
paul@3 | 661 | } |
paul@3 | 662 | |
paul@3 | 663 | static void EPD_Pin_PANEL_ON(EPD_pinstate pin) |
paul@3 | 664 | { |
paul@3 | 665 | if (HIGH == pin) { |
paul@3 | 666 | bsp_pinSet(13); |
paul@3 | 667 | } |
paul@3 | 668 | else { |
paul@3 | 669 | bsp_pinClr(13); |
paul@3 | 670 | } |
paul@3 | 671 | } |
paul@3 | 672 | |
paul@3 | 673 | static void EPD_Pin_DISCHARGE(EPD_pinstate pin) |
paul@3 | 674 | { |
paul@3 | 675 | if (HIGH == pin) { |
paul@3 | 676 | bsp_pinSet(14); |
paul@3 | 677 | } |
paul@3 | 678 | else { |
paul@3 | 679 | bsp_pinClr(14); |
paul@3 | 680 | } |
paul@3 | 681 | } |
paul@3 | 682 | |
paul@3 | 683 | static void EPD_Pin_BORDER(EPD_pinstate pin) |
paul@3 | 684 | { |
paul@3 | 685 | if (HIGH == pin) { |
paul@3 | 686 | bsp_pinSet(8); |
paul@3 | 687 | } |
paul@3 | 688 | else { |
paul@3 | 689 | bsp_pinClr(8); |
paul@3 | 690 | } |
paul@3 | 691 | } |
paul@3 | 692 | |
paul@3 | 693 | static EPD_pinstate epd_get_busy(void) |
paul@3 | 694 | { |
paul@3 | 695 | if (bsp_pinState(7)) |
paul@3 | 696 | return HIGH; |
paul@3 | 697 | else |
paul@3 | 698 | return LOW; |
paul@3 | 699 | } |
paul@3 | 700 | |
paul@3 | 701 | static void epd_pwm_active(uint16_t delayInMs) |
paul@3 | 702 | { |
paul@3 | 703 | uint16_t numOfIterations; |
paul@3 | 704 | |
paul@3 | 705 | numOfIterations = delayInMs * 100; |
paul@3 | 706 | |
paul@3 | 707 | for(; numOfIterations > 0; numOfIterations--) |
paul@3 | 708 | { |
paul@3 | 709 | bsp_pinSet(11); |
paul@3 | 710 | bsp_delayUs(5); //100kHz |
paul@3 | 711 | bsp_pinClr(11); |
paul@3 | 712 | bsp_delayUs(5); |
paul@3 | 713 | } |
paul@3 | 714 | } |