2 Copyright 2018 Massdrop Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "arm_atsam_protocol.h"
26 #include "config_led.h"
29 #define I2C_LED_USE_DMA 1 //Set 1 to use background DMA transfers for leds, Set 0 to use inline software transfers
31 static uint8_t i2c_led_q[I2C_Q_SIZE]; //I2C queue circular buffer
32 static uint8_t i2c_led_q_s; //Start of circular buffer
33 static uint8_t i2c_led_q_e; //End of circular buffer
34 static uint8_t i2c_led_q_full; //Queue full counter for reset
36 static uint8_t dma_sendbuf[I2C_DMA_MAX_SEND]; //Data being written to I2C
38 volatile uint8_t i2c_led_q_running;
40 #endif //MD_BOOTLOADER
44 DBGC(DC_I2C0_INIT_BEGIN);
46 CLK_set_i2c0_freq(CHAN_SERCOM_I2C0, FREQ_I2C0_DEFAULT);
49 PORT->Group[0].PMUX[4].bit.PMUXE = 2;
50 PORT->Group[0].PMUX[4].bit.PMUXO = 2;
51 PORT->Group[0].PINCFG[8].bit.PMUXEN = 1;
52 PORT->Group[0].PINCFG[9].bit.PMUXEN = 1;
55 //Note: SW Reset handled in CLK_set_i2c0_freq clks.c
57 SERCOM0->I2CM.CTRLA.bit.MODE = 5; //Set master mode
59 SERCOM0->I2CM.CTRLA.bit.SPEED = 0; //Set to 1 for Fast-mode Plus (FM+) up to 1 MHz
60 SERCOM0->I2CM.CTRLA.bit.RUNSTDBY = 1; //Enabled
62 SERCOM0->I2CM.CTRLA.bit.ENABLE = 1; //Enable the device
63 while (SERCOM0->I2CM.SYNCBUSY.bit.ENABLE) { DBGC(DC_I2C0_INIT_SYNC_ENABLING); } //Wait for SYNCBUSY.ENABLE to clear
65 SERCOM0->I2CM.STATUS.bit.BUSSTATE = 1; //Force into IDLE state
66 while (SERCOM0->I2CM.SYNCBUSY.bit.SYSOP) { DBGC(DC_I2C0_INIT_SYNC_SYSOP); }
67 while (SERCOM0->I2CM.STATUS.bit.BUSSTATE != 1) { DBGC(DC_I2C0_INIT_WAIT_IDLE); } //Wait while not idle
69 DBGC(DC_I2C0_INIT_COMPLETE);
72 uint8_t i2c0_start(uint8_t address)
74 SERCOM0->I2CM.ADDR.bit.ADDR = address;
75 while (SERCOM0->I2CM.SYNCBUSY.bit.SYSOP) {}
76 while (SERCOM0->I2CM.INTFLAG.bit.MB == 0) {}
77 while (SERCOM0->I2CM.STATUS.bit.RXNACK) {}
82 uint8_t i2c0_transmit(uint8_t address, uint8_t *data, uint16_t length, uint16_t timeout)
84 if (!length) return 0;
90 SERCOM0->I2CM.DATA.bit.DATA = *data;
91 while (SERCOM0->I2CM.INTFLAG.bit.MB == 0) {}
92 while (SERCOM0->I2CM.STATUS.bit.RXNACK) {}
105 if (SERCOM0->I2CM.STATUS.bit.CLKHOLD || SERCOM0->I2CM.INTFLAG.bit.MB == 1 || SERCOM0->I2CM.STATUS.bit.BUSSTATE != 1)
107 SERCOM0->I2CM.CTRLB.bit.CMD = 3;
108 while (SERCOM0->I2CM.SYNCBUSY.bit.SYSOP);
109 while (SERCOM0->I2CM.STATUS.bit.CLKHOLD);
110 while (SERCOM0->I2CM.INTFLAG.bit.MB);
111 while (SERCOM0->I2CM.STATUS.bit.BUSSTATE != 1);
115 #ifndef MD_BOOTLOADER
118 DBGC(DC_I2C1_INIT_BEGIN);
120 CLK_set_i2c1_freq(CHAN_SERCOM_I2C1, FREQ_I2C1_DEFAULT);
123 PORT->Group[0].PMUX[8].bit.PMUXE = 2;
124 PORT->Group[0].PMUX[8].bit.PMUXO = 2;
125 PORT->Group[0].PINCFG[16].bit.PMUXEN = 1;
126 PORT->Group[0].PINCFG[17].bit.PMUXEN = 1;
129 //Note: SW Reset handled in CLK_set_i2c1_freq clks.c
131 SERCOM1->I2CM.CTRLA.bit.MODE = 5; //MODE: Set master mode (No sync)
132 SERCOM1->I2CM.CTRLA.bit.SPEED = 1; //SPEED: Fm+ up to 1MHz (No sync)
133 SERCOM1->I2CM.CTRLA.bit.RUNSTDBY = 1; //RUNSTBY: Enabled (No sync)
135 SERCOM1->I2CM.CTRLB.bit.SMEN = 1; //SMEN: Smart mode enabled (For DMA)(No sync)
137 NVIC_EnableIRQ(SERCOM1_0_IRQn);
138 SERCOM1->I2CM.INTENSET.bit.ERROR = 1;
140 SERCOM1->I2CM.CTRLA.bit.ENABLE = 1; //ENABLE: Enable the device (sync SYNCBUSY.ENABLE)
141 while (SERCOM1->I2CM.SYNCBUSY.bit.ENABLE) { DBGC(DC_I2C1_INIT_SYNC_ENABLING); } //Wait for SYNCBUSY.ENABLE to clear
143 SERCOM1->I2CM.STATUS.bit.BUSSTATE = 1; //BUSSTATE: Force into IDLE state (sync SYNCBUSY.SYSOP)
144 while (SERCOM1->I2CM.SYNCBUSY.bit.SYSOP) { DBGC(DC_I2C1_INIT_SYNC_SYSOP); }
145 while (SERCOM1->I2CM.STATUS.bit.BUSSTATE != 1) { DBGC(DC_I2C1_INIT_WAIT_IDLE); } //Wait while not idle
147 DBGC(DC_I2C1_INIT_COMPLETE);
150 uint8_t i2c1_start(uint8_t address)
152 SERCOM1->I2CM.ADDR.bit.ADDR = address;
153 while (SERCOM1->I2CM.SYNCBUSY.bit.SYSOP) {}
154 while (SERCOM1->I2CM.INTFLAG.bit.MB == 0) {}
155 while (SERCOM1->I2CM.STATUS.bit.RXNACK) {}
160 uint8_t i2c1_transmit(uint8_t address, uint8_t *data, uint16_t length, uint16_t timeout)
162 if (!length) return 0;
168 SERCOM1->I2CM.DATA.bit.DATA = *data;
169 while (SERCOM1->I2CM.INTFLAG.bit.MB == 0) {}
170 while (SERCOM1->I2CM.STATUS.bit.RXNACK) {}
183 if (SERCOM1->I2CM.STATUS.bit.CLKHOLD || SERCOM1->I2CM.INTFLAG.bit.MB == 1 || SERCOM1->I2CM.STATUS.bit.BUSSTATE != 1)
185 SERCOM1->I2CM.CTRLB.bit.CMD = 3;
186 while (SERCOM1->I2CM.SYNCBUSY.bit.SYSOP);
187 while (SERCOM1->I2CM.STATUS.bit.CLKHOLD);
188 while (SERCOM1->I2CM.INTFLAG.bit.MB);
189 while (SERCOM1->I2CM.STATUS.bit.BUSSTATE != 1);
193 void i2c_led_send_CRWL(uint8_t drvid)
195 uint8_t i2cdata[] = { ISSI3733_CMDRWL, ISSI3733_CMDRWL_WRITE_ENABLE_ONCE };
196 i2c1_transmit(issidrv[drvid].addr, i2cdata, sizeof(i2cdata), 0);
199 void i2c_led_select_page(uint8_t drvid, uint8_t pageno)
201 uint8_t i2cdata[] = { ISSI3733_CMDR, pageno };
202 i2c1_transmit(issidrv[drvid].addr, i2cdata, sizeof(i2cdata), 0);
205 void i2c_led_send_GCR(uint8_t drvid)
207 uint8_t i2cdata[] = { ISSI3733_GCCR, 0x00 };
209 if (gcr_actual > LED_GCR_MAX) gcr_actual = LED_GCR_MAX;
210 i2cdata[1] = gcr_actual;
212 i2c1_transmit(issidrv[drvid].addr, i2cdata, sizeof(i2cdata), 0);
215 void i2c_led_send_onoff(uint8_t drvid)
217 #if I2C_LED_USE_DMA != 1
218 if (!i2c_led_q_running)
221 i2c_led_send_CRWL(drvid);
222 i2c_led_select_page(drvid, 0);
223 #if I2C_LED_USE_DMA != 1
227 *issidrv[drvid].onoff = 0; //Force start location offset to zero
228 i2c1_transmit(issidrv[drvid].addr, issidrv[drvid].onoff, ISSI3733_PG0_BYTES, 0);
231 void i2c_led_send_mode_op_gcr(uint8_t drvid, uint8_t mode, uint8_t operation)
233 uint8_t i2cdata[] = { ISSI3733_CR, mode | operation, gcr_actual};
234 i2c1_transmit(issidrv[drvid].addr, i2cdata, sizeof(i2cdata), 0);
237 void i2c_led_send_pur_pdr(uint8_t drvid, uint8_t pur, uint8_t pdr)
239 uint8_t i2cdata[] = { ISSI3733_SWYR_PUR, pur, pdr };
241 i2c1_transmit(issidrv[drvid].addr, i2cdata, sizeof(i2cdata), 0);
244 void i2c_led_send_pwm(uint8_t drvid)
246 #if I2C_LED_USE_DMA != 1
247 if (!i2c_led_q_running)
250 i2c_led_send_CRWL(drvid);
251 i2c_led_select_page(drvid, 0);
252 #if I2C_LED_USE_DMA != 1
256 *issidrv[drvid].pwm = 0; //Force start location offset to zero
257 i2c1_transmit(issidrv[drvid].addr, issidrv[drvid].pwm, ISSI3733_PG1_BYTES, 0);
260 uint8_t I2C3733_Init_Control(void)
262 DBGC(DC_I2C3733_INIT_CONTROL_BEGIN);
264 //Hardware state shutdown on boot
265 //USB state machine will enable driver when communication is ready
266 I2C3733_Control_Set(0);
270 sr_exp_data.bit.IRST = 0;
275 DBGC(DC_I2C3733_INIT_CONTROL_COMPLETE);
280 uint8_t I2C3733_Init_Drivers(void)
282 DBGC(DC_I2C3733_INIT_DRIVERS_BEGIN);
284 gcr_actual = ISSI3733_GCR_DEFAULT;
285 gcr_actual_last = gcr_actual;
287 if (gcr_actual > LED_GCR_MAX) gcr_actual = LED_GCR_MAX;
288 gcr_desired = gcr_actual;
290 //Set up master device
291 i2c_led_send_CRWL(0);
292 i2c_led_select_page(0, 3);
293 i2c_led_send_mode_op_gcr(0, 0, ISSI3733_CR_SSD_NORMAL); //No SYNC due to brightness mismatch with second driver
295 //Set up slave device
296 i2c_led_send_CRWL(1);
297 i2c_led_select_page(1, 3);
298 i2c_led_send_mode_op_gcr(1, 0, ISSI3733_CR_SSD_NORMAL); //No SYNC due to brightness mismatch with first driver and slight flicker at rgb values 1,2
300 i2c_led_send_CRWL(0);
301 i2c_led_select_page(0, 3);
302 i2c_led_send_pur_pdr(0, ISSI3733_SWYR_PUR_8000, ISSI3733_CSXR_PDR_8000);
304 i2c_led_send_CRWL(1);
305 i2c_led_select_page(1, 3);
306 i2c_led_send_pur_pdr(1, ISSI3733_SWYR_PUR_8000, ISSI3733_CSXR_PDR_8000);
308 DBGC(DC_I2C3733_INIT_DRIVERS_COMPLETE);
313 void I2C_DMAC_LED_Init(void)
317 DBGC(DC_I2C_DMAC_LED_INIT_BEGIN);
320 dmac->CTRL.bit.DMAENABLE = 0; //Disable DMAC
321 while (dmac->CTRL.bit.DMAENABLE) {} //Wait for disabled state in case of ongoing transfers
322 dmac->CTRL.bit.SWRST = 1; //Software Reset DMAC
323 while (dmac->CTRL.bit.SWRST) {} //Wait for software reset to complete
326 dmac->BASEADDR.reg = (uint32_t)&dmac_desc; //Set descriptor base address
327 dmac->WRBADDR.reg = (uint32_t)&dmac_desc_wb; //Set descriptor write back address
328 dmac->CTRL.reg |= 0x0f00; //Handle all priorities (LVL0-3)
331 dmac->Channel[0].CHCTRLA.bit.ENABLE = 0; //Disable the channel
332 while (dmac->Channel[0].CHCTRLA.bit.ENABLE) {} //Wait for disabled state in case of ongoing transfers
333 dmac->Channel[0].CHCTRLA.bit.SWRST = 1; //Software Reset the channel
334 while (dmac->Channel[0].CHCTRLA.bit.SWRST) {} //Wait for software reset to complete
337 dmac->Channel[0].CHCTRLA.bit.THRESHOLD = 0; //1BEAT
338 dmac->Channel[0].CHCTRLA.bit.BURSTLEN = 0; //SINGLE
339 dmac->Channel[0].CHCTRLA.bit.TRIGACT = 2; //BURST
340 dmac->Channel[0].CHCTRLA.bit.TRIGSRC = SERCOM1_DMAC_ID_TX; //Trigger source
341 dmac->Channel[0].CHCTRLA.bit.RUNSTDBY = 1; //Run in standby
343 NVIC_EnableIRQ(DMAC_0_IRQn);
344 dmac->Channel[0].CHINTENSET.bit.TCMPL = 1;
345 dmac->Channel[0].CHINTENSET.bit.TERR = 1;
348 dmac->CTRL.bit.DMAENABLE = 1; //Enable DMAC
349 while (dmac->CTRL.bit.DMAENABLE == 0) {} //Wait for enable state
351 DBGC(DC_I2C_DMAC_LED_INIT_COMPLETE);
356 void I2C3733_Control_Set(uint8_t state)
358 DBGC(DC_I2C3733_CONTROL_SET_BEGIN);
360 sr_exp_data.bit.SDB_N = (state == 1 ? 1 : 0);
363 DBGC(DC_I2C3733_CONTROL_SET_COMPLETE);
366 void i2c_led_desc_defaults(void)
368 dmac_desc.BTCTRL.bit.STEPSIZE = 0; //SRCINC used in favor for auto 1 inc
369 dmac_desc.BTCTRL.bit.STEPSEL = 0; //SRCINC used in favor for auto 1 inc
370 dmac_desc.BTCTRL.bit.DSTINC = 0; //The Destination Address Increment is disabled
371 dmac_desc.BTCTRL.bit.SRCINC = 1; //The Source Address Increment is enabled (Inc by 1)
372 dmac_desc.BTCTRL.bit.BEATSIZE = 0; //8-bit bus transfer
373 dmac_desc.BTCTRL.bit.BLOCKACT = 0; //Channel will be disabled if it is the last block transfer in the transaction
374 dmac_desc.BTCTRL.bit.EVOSEL = 0; //Event generation disabled
375 dmac_desc.BTCTRL.bit.VALID = 1; //Set dmac valid
378 void i2c_led_prepare_send_dma(uint8_t *data, uint8_t len)
380 i2c_led_desc_defaults();
382 dmac_desc.BTCNT.reg = len;
383 dmac_desc.SRCADDR.reg = (uint32_t)data + len;
384 dmac_desc.DSTADDR.reg = (uint32_t)&SERCOM1->I2CM.DATA.reg;
385 dmac_desc.DESCADDR.reg = 0;
388 void i2c_led_begin_dma(uint8_t drvid)
390 DMAC->Channel[0].CHCTRLA.bit.ENABLE = 1; //Enable the channel
392 SERCOM1->I2CM.ADDR.reg = (dmac_desc.BTCNT.reg << 16) | 0x2000 | issidrv[drvid].addr; //Begin transfer
395 void i2c_led_send_CRWL_dma(uint8_t drvid)
397 *(dma_sendbuf+0) = ISSI3733_CMDRWL;
398 *(dma_sendbuf+1) = ISSI3733_CMDRWL_WRITE_ENABLE_ONCE;
399 i2c_led_prepare_send_dma(dma_sendbuf, 2);
401 i2c_led_begin_dma(drvid);
404 void i2c_led_select_page_dma(uint8_t drvid, uint8_t pageno)
406 *(dma_sendbuf+0) = ISSI3733_CMDR;
407 *(dma_sendbuf+1) = pageno;
408 i2c_led_prepare_send_dma(dma_sendbuf, 2);
410 i2c_led_begin_dma(drvid);
413 void i2c_led_send_GCR_dma(uint8_t drvid)
415 *(dma_sendbuf+0) = ISSI3733_GCCR;
416 *(dma_sendbuf+1) = gcr_actual;
417 i2c_led_prepare_send_dma(dma_sendbuf, 2);
419 i2c_led_begin_dma(drvid);
422 void i2c_led_send_pwm_dma(uint8_t drvid)
424 //Note: This copies the CURRENT pwm buffer, which may be getting modified
425 memcpy(dma_sendbuf, issidrv[drvid].pwm, ISSI3733_PG1_BYTES);
426 *dma_sendbuf = 0; //Force start location offset to zero
427 i2c_led_prepare_send_dma(dma_sendbuf, ISSI3733_PG1_BYTES);
429 i2c_led_begin_dma(drvid);
432 void i2c_led_send_onoff_dma(uint8_t drvid)
434 //Note: This copies the CURRENT onoff buffer, which may be getting modified
435 memcpy(dma_sendbuf, issidrv[drvid].onoff, ISSI3733_PG0_BYTES);
436 *dma_sendbuf = 0; //Force start location offset to zero
437 i2c_led_prepare_send_dma(dma_sendbuf, ISSI3733_PG0_BYTES);
439 i2c_led_begin_dma(drvid);
442 void i2c_led_q_init(void)
444 memset(i2c_led_q, 0, I2C_Q_SIZE);
447 i2c_led_q_running = 0;
451 uint8_t i2c_led_q_isempty(void)
453 return i2c_led_q_s == i2c_led_q_e;
456 uint8_t i2c_led_q_size(void)
458 return (i2c_led_q_e - i2c_led_q_s) % I2C_Q_SIZE;
461 uint8_t i2c_led_q_available(void)
463 return I2C_Q_SIZE - i2c_led_q_size() - 1; //Never allow end to meet start
466 void i2c_led_q_add(uint8_t cmd)
468 //WARNING: Always request room before adding commands!
471 i2c_led_q[i2c_led_q_e] = cmd;
473 i2c_led_q_e = (i2c_led_q_e + 1) % I2C_Q_SIZE; //Move end up one or wrap
476 void i2c_led_q_s_advance(void)
478 i2c_led_q_s = (i2c_led_q_s + 1) % I2C_Q_SIZE; //Move start up one or wrap
481 //Always request room before adding commands
482 //PS: In case the queue somehow gets filled, it will reset if it can not clear up
483 //PS: Could only get this to happen through unrealistic timings to overload the I2C bus
484 uint8_t i2c_led_q_request_room(uint8_t request_size)
486 if (request_size > i2c_led_q_available())
490 if (i2c_led_q_full >= 100) //Give the queue a chance to clear up
506 uint8_t i2c_led_q_run(void)
508 if (i2c_led_q_isempty())
510 i2c_led_q_running = 0;
514 if (i2c_led_q_running) return 1;
516 i2c_led_q_running = 1;
518 #if I2C_LED_USE_DMA != 1
519 while (!i2c_led_q_isempty())
523 if (i2c_led_q[i2c_led_q_s] == I2C_Q_CRWL)
525 i2c_led_q_s_advance();
526 uint8_t drvid = i2c_led_q[i2c_led_q_s];
527 #if I2C_LED_USE_DMA == 1
528 i2c_led_send_CRWL_dma(drvid);
530 i2c_led_send_CRWL(drvid);
533 else if (i2c_led_q[i2c_led_q_s] == I2C_Q_PAGE_SELECT)
535 i2c_led_q_s_advance();
536 uint8_t drvid = i2c_led_q[i2c_led_q_s];
537 i2c_led_q_s_advance();
538 uint8_t page = i2c_led_q[i2c_led_q_s];
539 #if I2C_LED_USE_DMA == 1
540 i2c_led_select_page_dma(drvid, page);
542 i2c_led_select_page(drvid, page);
545 else if (i2c_led_q[i2c_led_q_s] == I2C_Q_PWM)
547 i2c_led_q_s_advance();
548 uint8_t drvid = i2c_led_q[i2c_led_q_s];
549 #if I2C_LED_USE_DMA == 1
550 i2c_led_send_pwm_dma(drvid);
552 i2c_led_send_pwm(drvid);
555 else if (i2c_led_q[i2c_led_q_s] == I2C_Q_GCR)
557 i2c_led_q_s_advance();
558 uint8_t drvid = i2c_led_q[i2c_led_q_s];
559 #if I2C_LED_USE_DMA == 1
560 i2c_led_send_GCR_dma(drvid);
562 i2c_led_send_GCR(drvid);
565 else if (i2c_led_q[i2c_led_q_s] == I2C_Q_ONOFF)
567 i2c_led_q_s_advance();
568 uint8_t drvid = i2c_led_q[i2c_led_q_s];
569 #if I2C_LED_USE_DMA == 1
570 i2c_led_send_onoff_dma(drvid);
572 i2c_led_send_onoff(drvid);
576 i2c_led_q_s_advance(); //Advance last run command or if the command byte was not serviced
578 #if I2C_LED_USE_DMA != 1
581 i2c_led_q_running = 0;
586 #endif //MD_BOOTLOADER