2018-08-29 19:07:52 +00:00
|
|
|
/*
|
|
|
|
Copyright 2018 Massdrop Inc.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "arm_atsam_protocol.h"
|
|
|
|
|
2019-04-04 01:30:47 +00:00
|
|
|
#if !defined(MD_BOOTLOADER) && defined(RGB_MATRIX_ENABLE)
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
# include <string.h>
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
// From keyboard
|
|
|
|
# include "config.h"
|
|
|
|
# include "config_led.h"
|
|
|
|
# include "matrix.h"
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
# define I2C_LED_USE_DMA 1 // Set 1 to use background DMA transfers for leds, Set 0 to use inline software transfers
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
static uint8_t i2c_led_q[I2C_Q_SIZE]; // I2C queue circular buffer
|
|
|
|
static uint8_t i2c_led_q_s; // Start of circular buffer
|
|
|
|
static uint8_t i2c_led_q_e; // End of circular buffer
|
|
|
|
static uint8_t i2c_led_q_full; // Queue full counter for reset
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
static uint8_t dma_sendbuf[I2C_DMA_MAX_SEND]; // Data being written to I2C
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
volatile uint8_t i2c_led_q_running;
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
#endif // !defined(MD_BOOTLOADER) && defined(RGB_MATRIX_ENABLE)
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c0_init(void) {
|
2018-08-29 19:07:52 +00:00
|
|
|
DBGC(DC_I2C0_INIT_BEGIN);
|
|
|
|
|
|
|
|
CLK_set_i2c0_freq(CHAN_SERCOM_I2C0, FREQ_I2C0_DEFAULT);
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
// MCU
|
|
|
|
PORT->Group[0].PMUX[4].bit.PMUXE = 2;
|
|
|
|
PORT->Group[0].PMUX[4].bit.PMUXO = 2;
|
2018-08-29 19:07:52 +00:00
|
|
|
PORT->Group[0].PINCFG[8].bit.PMUXEN = 1;
|
|
|
|
PORT->Group[0].PINCFG[9].bit.PMUXEN = 1;
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
// I2C
|
|
|
|
// Note: SW Reset handled in CLK_set_i2c0_freq clks.c
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
SERCOM0->I2CM.CTRLA.bit.MODE = 5; // Set master mode
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
SERCOM0->I2CM.CTRLA.bit.SPEED = 0; // Set to 1 for Fast-mode Plus (FM+) up to 1 MHz
|
|
|
|
SERCOM0->I2CM.CTRLA.bit.RUNSTDBY = 1; // Enabled
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
SERCOM0->I2CM.CTRLA.bit.ENABLE = 1; // Enable the device
|
|
|
|
while (SERCOM0->I2CM.SYNCBUSY.bit.ENABLE) {
|
|
|
|
DBGC(DC_I2C0_INIT_SYNC_ENABLING);
|
|
|
|
} // Wait for SYNCBUSY.ENABLE to clear
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
SERCOM0->I2CM.STATUS.bit.BUSSTATE = 1; // Force into IDLE state
|
|
|
|
while (SERCOM0->I2CM.SYNCBUSY.bit.SYSOP) {
|
|
|
|
DBGC(DC_I2C0_INIT_SYNC_SYSOP);
|
|
|
|
}
|
|
|
|
while (SERCOM0->I2CM.STATUS.bit.BUSSTATE != 1) {
|
|
|
|
DBGC(DC_I2C0_INIT_WAIT_IDLE);
|
|
|
|
} // Wait while not idle
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
DBGC(DC_I2C0_INIT_COMPLETE);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
uint8_t i2c0_start(uint8_t address) {
|
2018-08-29 19:07:52 +00:00
|
|
|
SERCOM0->I2CM.ADDR.bit.ADDR = address;
|
2019-08-30 18:19:03 +00:00
|
|
|
while (SERCOM0->I2CM.SYNCBUSY.bit.SYSOP) {
|
|
|
|
}
|
|
|
|
while (SERCOM0->I2CM.INTFLAG.bit.MB == 0) {
|
|
|
|
}
|
|
|
|
while (SERCOM0->I2CM.STATUS.bit.RXNACK) {
|
|
|
|
}
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
uint8_t i2c0_transmit(uint8_t address, uint8_t *data, uint16_t length, uint16_t timeout) {
|
2018-08-29 19:07:52 +00:00
|
|
|
if (!length) return 0;
|
|
|
|
|
|
|
|
i2c0_start(address);
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
while (length) {
|
2018-08-29 19:07:52 +00:00
|
|
|
SERCOM0->I2CM.DATA.bit.DATA = *data;
|
2019-08-30 18:19:03 +00:00
|
|
|
while (SERCOM0->I2CM.INTFLAG.bit.MB == 0) {
|
|
|
|
}
|
|
|
|
while (SERCOM0->I2CM.STATUS.bit.RXNACK) {
|
|
|
|
}
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
data++;
|
|
|
|
length--;
|
|
|
|
}
|
|
|
|
|
|
|
|
i2c0_stop();
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c0_stop(void) {
|
|
|
|
if (SERCOM0->I2CM.STATUS.bit.CLKHOLD || SERCOM0->I2CM.INTFLAG.bit.MB == 1 || SERCOM0->I2CM.STATUS.bit.BUSSTATE != 1) {
|
2018-08-29 19:07:52 +00:00
|
|
|
SERCOM0->I2CM.CTRLB.bit.CMD = 3;
|
2019-08-30 18:19:03 +00:00
|
|
|
while (SERCOM0->I2CM.SYNCBUSY.bit.SYSOP)
|
|
|
|
;
|
|
|
|
while (SERCOM0->I2CM.STATUS.bit.CLKHOLD)
|
|
|
|
;
|
|
|
|
while (SERCOM0->I2CM.INTFLAG.bit.MB)
|
|
|
|
;
|
|
|
|
while (SERCOM0->I2CM.STATUS.bit.BUSSTATE != 1)
|
|
|
|
;
|
2018-08-29 19:07:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-04 01:30:47 +00:00
|
|
|
#if !defined(MD_BOOTLOADER) && defined(RGB_MATRIX_ENABLE)
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c1_init(void) {
|
2018-08-29 19:07:52 +00:00
|
|
|
DBGC(DC_I2C1_INIT_BEGIN);
|
|
|
|
|
|
|
|
CLK_set_i2c1_freq(CHAN_SERCOM_I2C1, FREQ_I2C1_DEFAULT);
|
|
|
|
|
|
|
|
/* MCU */
|
2019-08-30 18:19:03 +00:00
|
|
|
PORT->Group[0].PMUX[8].bit.PMUXE = 2;
|
|
|
|
PORT->Group[0].PMUX[8].bit.PMUXO = 2;
|
2018-08-29 19:07:52 +00:00
|
|
|
PORT->Group[0].PINCFG[16].bit.PMUXEN = 1;
|
|
|
|
PORT->Group[0].PINCFG[17].bit.PMUXEN = 1;
|
|
|
|
|
|
|
|
/* I2C */
|
2019-08-30 18:19:03 +00:00
|
|
|
// Note: SW Reset handled in CLK_set_i2c1_freq clks.c
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
SERCOM1->I2CM.CTRLA.bit.MODE = 5; // MODE: Set master mode (No sync)
|
|
|
|
SERCOM1->I2CM.CTRLA.bit.SPEED = 1; // SPEED: Fm+ up to 1MHz (No sync)
|
|
|
|
SERCOM1->I2CM.CTRLA.bit.RUNSTDBY = 1; // RUNSTBY: Enabled (No sync)
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
SERCOM1->I2CM.CTRLB.bit.SMEN = 1; // SMEN: Smart mode enabled (For DMA)(No sync)
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
NVIC_EnableIRQ(SERCOM1_0_IRQn);
|
|
|
|
SERCOM1->I2CM.INTENSET.bit.ERROR = 1;
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
SERCOM1->I2CM.CTRLA.bit.ENABLE = 1; // ENABLE: Enable the device (sync SYNCBUSY.ENABLE)
|
|
|
|
while (SERCOM1->I2CM.SYNCBUSY.bit.ENABLE) {
|
|
|
|
DBGC(DC_I2C1_INIT_SYNC_ENABLING);
|
|
|
|
} // Wait for SYNCBUSY.ENABLE to clear
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
SERCOM1->I2CM.STATUS.bit.BUSSTATE = 1; // BUSSTATE: Force into IDLE state (sync SYNCBUSY.SYSOP)
|
|
|
|
while (SERCOM1->I2CM.SYNCBUSY.bit.SYSOP) {
|
|
|
|
DBGC(DC_I2C1_INIT_SYNC_SYSOP);
|
|
|
|
}
|
|
|
|
while (SERCOM1->I2CM.STATUS.bit.BUSSTATE != 1) {
|
|
|
|
DBGC(DC_I2C1_INIT_WAIT_IDLE);
|
|
|
|
} // Wait while not idle
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
DBGC(DC_I2C1_INIT_COMPLETE);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
uint8_t i2c1_start(uint8_t address) {
|
2018-08-29 19:07:52 +00:00
|
|
|
SERCOM1->I2CM.ADDR.bit.ADDR = address;
|
2019-08-30 18:19:03 +00:00
|
|
|
while (SERCOM1->I2CM.SYNCBUSY.bit.SYSOP) {
|
|
|
|
}
|
|
|
|
while (SERCOM1->I2CM.INTFLAG.bit.MB == 0) {
|
|
|
|
}
|
|
|
|
while (SERCOM1->I2CM.STATUS.bit.RXNACK) {
|
|
|
|
}
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
uint8_t i2c1_transmit(uint8_t address, uint8_t *data, uint16_t length, uint16_t timeout) {
|
2018-08-29 19:07:52 +00:00
|
|
|
if (!length) return 0;
|
|
|
|
|
|
|
|
i2c1_start(address);
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
while (length) {
|
2018-08-29 19:07:52 +00:00
|
|
|
SERCOM1->I2CM.DATA.bit.DATA = *data;
|
2019-08-30 18:19:03 +00:00
|
|
|
while (SERCOM1->I2CM.INTFLAG.bit.MB == 0) {
|
|
|
|
}
|
|
|
|
while (SERCOM1->I2CM.STATUS.bit.RXNACK) {
|
|
|
|
}
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
data++;
|
|
|
|
length--;
|
|
|
|
}
|
|
|
|
|
|
|
|
i2c1_stop();
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c1_stop(void) {
|
|
|
|
if (SERCOM1->I2CM.STATUS.bit.CLKHOLD || SERCOM1->I2CM.INTFLAG.bit.MB == 1 || SERCOM1->I2CM.STATUS.bit.BUSSTATE != 1) {
|
2018-08-29 19:07:52 +00:00
|
|
|
SERCOM1->I2CM.CTRLB.bit.CMD = 3;
|
2019-08-30 18:19:03 +00:00
|
|
|
while (SERCOM1->I2CM.SYNCBUSY.bit.SYSOP)
|
|
|
|
;
|
|
|
|
while (SERCOM1->I2CM.STATUS.bit.CLKHOLD)
|
|
|
|
;
|
|
|
|
while (SERCOM1->I2CM.INTFLAG.bit.MB)
|
|
|
|
;
|
|
|
|
while (SERCOM1->I2CM.STATUS.bit.BUSSTATE != 1)
|
|
|
|
;
|
2018-08-29 19:07:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_send_CRWL(uint8_t drvid) {
|
|
|
|
uint8_t i2cdata[] = {ISSI3733_CMDRWL, ISSI3733_CMDRWL_WRITE_ENABLE_ONCE};
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c1_transmit(issidrv[drvid].addr, i2cdata, sizeof(i2cdata), 0);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_select_page(uint8_t drvid, uint8_t pageno) {
|
|
|
|
uint8_t i2cdata[] = {ISSI3733_CMDR, pageno};
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c1_transmit(issidrv[drvid].addr, i2cdata, sizeof(i2cdata), 0);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_send_GCR(uint8_t drvid) {
|
|
|
|
uint8_t i2cdata[] = {ISSI3733_GCCR, 0x00};
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
if (gcr_actual > LED_GCR_MAX) gcr_actual = LED_GCR_MAX;
|
|
|
|
i2cdata[1] = gcr_actual;
|
|
|
|
|
|
|
|
i2c1_transmit(issidrv[drvid].addr, i2cdata, sizeof(i2cdata), 0);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_send_onoff(uint8_t drvid) {
|
|
|
|
# if I2C_LED_USE_DMA != 1
|
|
|
|
if (!i2c_led_q_running) {
|
|
|
|
# endif
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_send_CRWL(drvid);
|
|
|
|
i2c_led_select_page(drvid, 0);
|
2019-08-30 18:19:03 +00:00
|
|
|
# if I2C_LED_USE_DMA != 1
|
2018-08-29 19:07:52 +00:00
|
|
|
}
|
2019-08-30 18:19:03 +00:00
|
|
|
# endif
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
*issidrv[drvid].onoff = 0; // Force start location offset to zero
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c1_transmit(issidrv[drvid].addr, issidrv[drvid].onoff, ISSI3733_PG0_BYTES, 0);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_send_mode_op_gcr(uint8_t drvid, uint8_t mode, uint8_t operation) {
|
|
|
|
uint8_t i2cdata[] = {ISSI3733_CR, mode | operation, gcr_actual};
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c1_transmit(issidrv[drvid].addr, i2cdata, sizeof(i2cdata), 0);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_send_pur_pdr(uint8_t drvid, uint8_t pur, uint8_t pdr) {
|
|
|
|
uint8_t i2cdata[] = {ISSI3733_SWYR_PUR, pur, pdr};
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
i2c1_transmit(issidrv[drvid].addr, i2cdata, sizeof(i2cdata), 0);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_send_pwm(uint8_t drvid) {
|
|
|
|
# if I2C_LED_USE_DMA != 1
|
|
|
|
if (!i2c_led_q_running) {
|
|
|
|
# endif
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_send_CRWL(drvid);
|
|
|
|
i2c_led_select_page(drvid, 0);
|
2019-08-30 18:19:03 +00:00
|
|
|
# if I2C_LED_USE_DMA != 1
|
2018-08-29 19:07:52 +00:00
|
|
|
}
|
2019-08-30 18:19:03 +00:00
|
|
|
# endif
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
*issidrv[drvid].pwm = 0; // Force start location offset to zero
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c1_transmit(issidrv[drvid].addr, issidrv[drvid].pwm, ISSI3733_PG1_BYTES, 0);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
uint8_t I2C3733_Init_Control(void) {
|
2018-08-29 19:07:52 +00:00
|
|
|
DBGC(DC_I2C3733_INIT_CONTROL_BEGIN);
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
// Hardware state shutdown on boot
|
|
|
|
// USB state machine will enable driver when communication is ready
|
2018-11-02 19:30:51 +00:00
|
|
|
I2C3733_Control_Set(0);
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2018-12-18 20:21:25 +00:00
|
|
|
wait_ms(1);
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2018-12-10 19:28:06 +00:00
|
|
|
sr_exp_data.bit.IRST = 0;
|
|
|
|
SR_EXP_WriteData();
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2018-12-18 20:21:25 +00:00
|
|
|
wait_ms(1);
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
DBGC(DC_I2C3733_INIT_CONTROL_COMPLETE);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
uint8_t I2C3733_Init_Drivers(void) {
|
2018-08-29 19:07:52 +00:00
|
|
|
DBGC(DC_I2C3733_INIT_DRIVERS_BEGIN);
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
gcr_actual = ISSI3733_GCR_DEFAULT;
|
2018-08-29 19:07:52 +00:00
|
|
|
gcr_actual_last = gcr_actual;
|
|
|
|
|
|
|
|
if (gcr_actual > LED_GCR_MAX) gcr_actual = LED_GCR_MAX;
|
|
|
|
gcr_desired = gcr_actual;
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
// Set up master device
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_send_CRWL(0);
|
|
|
|
i2c_led_select_page(0, 3);
|
2019-08-30 18:19:03 +00:00
|
|
|
i2c_led_send_mode_op_gcr(0, 0, ISSI3733_CR_SSD_NORMAL); // No SYNC due to brightness mismatch with second driver
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
// Set up slave device
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_send_CRWL(1);
|
|
|
|
i2c_led_select_page(1, 3);
|
2019-08-30 18:19:03 +00:00
|
|
|
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
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
i2c_led_send_CRWL(0);
|
|
|
|
i2c_led_select_page(0, 3);
|
|
|
|
i2c_led_send_pur_pdr(0, ISSI3733_SWYR_PUR_8000, ISSI3733_CSXR_PDR_8000);
|
|
|
|
|
|
|
|
i2c_led_send_CRWL(1);
|
|
|
|
i2c_led_select_page(1, 3);
|
|
|
|
i2c_led_send_pur_pdr(1, ISSI3733_SWYR_PUR_8000, ISSI3733_CSXR_PDR_8000);
|
|
|
|
|
|
|
|
DBGC(DC_I2C3733_INIT_DRIVERS_COMPLETE);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void I2C_DMAC_LED_Init(void) {
|
2018-08-29 19:07:52 +00:00
|
|
|
Dmac *dmac = DMAC;
|
|
|
|
|
|
|
|
DBGC(DC_I2C_DMAC_LED_INIT_BEGIN);
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
// Disable device
|
|
|
|
dmac->CTRL.bit.DMAENABLE = 0; // Disable DMAC
|
|
|
|
while (dmac->CTRL.bit.DMAENABLE) {
|
|
|
|
} // Wait for disabled state in case of ongoing transfers
|
|
|
|
dmac->CTRL.bit.SWRST = 1; // Software Reset DMAC
|
|
|
|
while (dmac->CTRL.bit.SWRST) {
|
|
|
|
} // Wait for software reset to complete
|
|
|
|
|
|
|
|
// Configure device
|
|
|
|
dmac->BASEADDR.reg = (uint32_t)&dmac_desc; // Set descriptor base address
|
|
|
|
dmac->WRBADDR.reg = (uint32_t)&dmac_desc_wb; // Set descriptor write back address
|
|
|
|
dmac->CTRL.reg |= 0x0f00; // Handle all priorities (LVL0-3)
|
|
|
|
|
|
|
|
// Disable channel
|
|
|
|
dmac->Channel[0].CHCTRLA.bit.ENABLE = 0; // Disable the channel
|
|
|
|
while (dmac->Channel[0].CHCTRLA.bit.ENABLE) {
|
|
|
|
} // Wait for disabled state in case of ongoing transfers
|
|
|
|
dmac->Channel[0].CHCTRLA.bit.SWRST = 1; // Software Reset the channel
|
|
|
|
while (dmac->Channel[0].CHCTRLA.bit.SWRST) {
|
|
|
|
} // Wait for software reset to complete
|
|
|
|
|
|
|
|
// Configure channel
|
|
|
|
dmac->Channel[0].CHCTRLA.bit.THRESHOLD = 0; // 1BEAT
|
|
|
|
dmac->Channel[0].CHCTRLA.bit.BURSTLEN = 0; // SINGLE
|
|
|
|
dmac->Channel[0].CHCTRLA.bit.TRIGACT = 2; // BURST
|
|
|
|
dmac->Channel[0].CHCTRLA.bit.TRIGSRC = SERCOM1_DMAC_ID_TX; // Trigger source
|
|
|
|
dmac->Channel[0].CHCTRLA.bit.RUNSTDBY = 1; // Run in standby
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
NVIC_EnableIRQ(DMAC_0_IRQn);
|
|
|
|
dmac->Channel[0].CHINTENSET.bit.TCMPL = 1;
|
2019-08-30 18:19:03 +00:00
|
|
|
dmac->Channel[0].CHINTENSET.bit.TERR = 1;
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
// Enable device
|
|
|
|
dmac->CTRL.bit.DMAENABLE = 1; // Enable DMAC
|
|
|
|
while (dmac->CTRL.bit.DMAENABLE == 0) {
|
|
|
|
} // Wait for enable state
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
DBGC(DC_I2C_DMAC_LED_INIT_COMPLETE);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
// state = 1 enable
|
|
|
|
// state = 0 disable
|
|
|
|
void I2C3733_Control_Set(uint8_t state) {
|
2018-08-29 19:07:52 +00:00
|
|
|
DBGC(DC_I2C3733_CONTROL_SET_BEGIN);
|
|
|
|
|
2018-12-10 19:28:06 +00:00
|
|
|
sr_exp_data.bit.SDB_N = (state == 1 ? 1 : 0);
|
|
|
|
SR_EXP_WriteData();
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
DBGC(DC_I2C3733_CONTROL_SET_COMPLETE);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_desc_defaults(void) {
|
|
|
|
dmac_desc.BTCTRL.bit.STEPSIZE = 0; // SRCINC used in favor for auto 1 inc
|
|
|
|
dmac_desc.BTCTRL.bit.STEPSEL = 0; // SRCINC used in favor for auto 1 inc
|
|
|
|
dmac_desc.BTCTRL.bit.DSTINC = 0; // The Destination Address Increment is disabled
|
|
|
|
dmac_desc.BTCTRL.bit.SRCINC = 1; // The Source Address Increment is enabled (Inc by 1)
|
|
|
|
dmac_desc.BTCTRL.bit.BEATSIZE = 0; // 8-bit bus transfer
|
|
|
|
dmac_desc.BTCTRL.bit.BLOCKACT = 0; // Channel will be disabled if it is the last block transfer in the transaction
|
|
|
|
dmac_desc.BTCTRL.bit.EVOSEL = 0; // Event generation disabled
|
|
|
|
dmac_desc.BTCTRL.bit.VALID = 1; // Set dmac valid
|
2018-08-29 19:07:52 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_prepare_send_dma(uint8_t *data, uint8_t len) {
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_desc_defaults();
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
dmac_desc.BTCNT.reg = len;
|
|
|
|
dmac_desc.SRCADDR.reg = (uint32_t)data + len;
|
|
|
|
dmac_desc.DSTADDR.reg = (uint32_t)&SERCOM1->I2CM.DATA.reg;
|
2018-08-29 19:07:52 +00:00
|
|
|
dmac_desc.DESCADDR.reg = 0;
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_begin_dma(uint8_t drvid) {
|
|
|
|
DMAC->Channel[0].CHCTRLA.bit.ENABLE = 1; // Enable the channel
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
SERCOM1->I2CM.ADDR.reg = (dmac_desc.BTCNT.reg << 16) | 0x2000 | issidrv[drvid].addr; // Begin transfer
|
2018-08-29 19:07:52 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_send_CRWL_dma(uint8_t drvid) {
|
|
|
|
*(dma_sendbuf + 0) = ISSI3733_CMDRWL;
|
|
|
|
*(dma_sendbuf + 1) = ISSI3733_CMDRWL_WRITE_ENABLE_ONCE;
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_prepare_send_dma(dma_sendbuf, 2);
|
|
|
|
|
|
|
|
i2c_led_begin_dma(drvid);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_select_page_dma(uint8_t drvid, uint8_t pageno) {
|
|
|
|
*(dma_sendbuf + 0) = ISSI3733_CMDR;
|
|
|
|
*(dma_sendbuf + 1) = pageno;
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_prepare_send_dma(dma_sendbuf, 2);
|
|
|
|
|
|
|
|
i2c_led_begin_dma(drvid);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_send_GCR_dma(uint8_t drvid) {
|
|
|
|
*(dma_sendbuf + 0) = ISSI3733_GCCR;
|
|
|
|
*(dma_sendbuf + 1) = gcr_actual;
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_prepare_send_dma(dma_sendbuf, 2);
|
|
|
|
|
|
|
|
i2c_led_begin_dma(drvid);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_send_pwm_dma(uint8_t drvid) {
|
|
|
|
// Note: This copies the CURRENT pwm buffer, which may be getting modified
|
2018-08-29 19:07:52 +00:00
|
|
|
memcpy(dma_sendbuf, issidrv[drvid].pwm, ISSI3733_PG1_BYTES);
|
2019-08-30 18:19:03 +00:00
|
|
|
*dma_sendbuf = 0; // Force start location offset to zero
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_prepare_send_dma(dma_sendbuf, ISSI3733_PG1_BYTES);
|
|
|
|
|
|
|
|
i2c_led_begin_dma(drvid);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_send_onoff_dma(uint8_t drvid) {
|
|
|
|
// Note: This copies the CURRENT onoff buffer, which may be getting modified
|
2018-08-29 19:07:52 +00:00
|
|
|
memcpy(dma_sendbuf, issidrv[drvid].onoff, ISSI3733_PG0_BYTES);
|
2019-08-30 18:19:03 +00:00
|
|
|
*dma_sendbuf = 0; // Force start location offset to zero
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_prepare_send_dma(dma_sendbuf, ISSI3733_PG0_BYTES);
|
|
|
|
|
|
|
|
i2c_led_begin_dma(drvid);
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_q_init(void) {
|
2018-08-29 19:07:52 +00:00
|
|
|
memset(i2c_led_q, 0, I2C_Q_SIZE);
|
2019-08-30 18:19:03 +00:00
|
|
|
i2c_led_q_s = 0;
|
|
|
|
i2c_led_q_e = 0;
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_q_running = 0;
|
2019-08-30 18:19:03 +00:00
|
|
|
i2c_led_q_full = 0;
|
2018-08-29 19:07:52 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
uint8_t i2c_led_q_isempty(void) { return i2c_led_q_s == i2c_led_q_e; }
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
uint8_t i2c_led_q_size(void) { return (i2c_led_q_e - i2c_led_q_s) % I2C_Q_SIZE; }
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
uint8_t i2c_led_q_available(void) {
|
|
|
|
return I2C_Q_SIZE - i2c_led_q_size() - 1; // Never allow end to meet start
|
2018-08-29 19:07:52 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_q_add(uint8_t cmd) {
|
|
|
|
// WARNING: Always request room before adding commands!
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
// Assign command
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_q[i2c_led_q_e] = cmd;
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
i2c_led_q_e = (i2c_led_q_e + 1) % I2C_Q_SIZE; // Move end up one or wrap
|
2018-08-29 19:07:52 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
void i2c_led_q_s_advance(void) {
|
|
|
|
i2c_led_q_s = (i2c_led_q_s + 1) % I2C_Q_SIZE; // Move start up one or wrap
|
2018-08-29 19:07:52 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
// Always request room before adding commands
|
|
|
|
// PS: In case the queue somehow gets filled, it will reset if it can not clear up
|
|
|
|
// PS: Could only get this to happen through unrealistic timings to overload the I2C bus
|
|
|
|
uint8_t i2c_led_q_request_room(uint8_t request_size) {
|
|
|
|
if (request_size > i2c_led_q_available()) {
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_q_full++;
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
if (i2c_led_q_full >= 100) // Give the queue a chance to clear up
|
2018-08-29 19:07:52 +00:00
|
|
|
{
|
2018-12-10 19:28:06 +00:00
|
|
|
DBG_LED_ON;
|
2018-08-29 19:07:52 +00:00
|
|
|
I2C_DMAC_LED_Init();
|
|
|
|
i2c_led_q_init();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
i2c_led_q_full = 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
uint8_t i2c_led_q_run(void) {
|
|
|
|
if (i2c_led_q_isempty()) {
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_q_running = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i2c_led_q_running) return 1;
|
|
|
|
|
|
|
|
i2c_led_q_running = 1;
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
# if I2C_LED_USE_DMA != 1
|
|
|
|
while (!i2c_led_q_isempty()) {
|
|
|
|
# endif
|
|
|
|
// run command
|
|
|
|
if (i2c_led_q[i2c_led_q_s] == I2C_Q_CRWL) {
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_q_s_advance();
|
|
|
|
uint8_t drvid = i2c_led_q[i2c_led_q_s];
|
2019-08-30 18:19:03 +00:00
|
|
|
# if I2C_LED_USE_DMA == 1
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_send_CRWL_dma(drvid);
|
2019-08-30 18:19:03 +00:00
|
|
|
# else
|
|
|
|
i2c_led_send_CRWL(drvid);
|
|
|
|
# endif
|
|
|
|
} else if (i2c_led_q[i2c_led_q_s] == I2C_Q_PAGE_SELECT) {
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_q_s_advance();
|
|
|
|
uint8_t drvid = i2c_led_q[i2c_led_q_s];
|
|
|
|
i2c_led_q_s_advance();
|
|
|
|
uint8_t page = i2c_led_q[i2c_led_q_s];
|
2019-08-30 18:19:03 +00:00
|
|
|
# if I2C_LED_USE_DMA == 1
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_select_page_dma(drvid, page);
|
2019-08-30 18:19:03 +00:00
|
|
|
# else
|
|
|
|
i2c_led_select_page(drvid, page);
|
|
|
|
# endif
|
|
|
|
} else if (i2c_led_q[i2c_led_q_s] == I2C_Q_PWM) {
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_q_s_advance();
|
|
|
|
uint8_t drvid = i2c_led_q[i2c_led_q_s];
|
2019-08-30 18:19:03 +00:00
|
|
|
# if I2C_LED_USE_DMA == 1
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_send_pwm_dma(drvid);
|
2019-08-30 18:19:03 +00:00
|
|
|
# else
|
|
|
|
i2c_led_send_pwm(drvid);
|
|
|
|
# endif
|
|
|
|
} else if (i2c_led_q[i2c_led_q_s] == I2C_Q_GCR) {
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_q_s_advance();
|
|
|
|
uint8_t drvid = i2c_led_q[i2c_led_q_s];
|
2019-08-30 18:19:03 +00:00
|
|
|
# if I2C_LED_USE_DMA == 1
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_send_GCR_dma(drvid);
|
2019-08-30 18:19:03 +00:00
|
|
|
# else
|
|
|
|
i2c_led_send_GCR(drvid);
|
|
|
|
# endif
|
|
|
|
} else if (i2c_led_q[i2c_led_q_s] == I2C_Q_ONOFF) {
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_q_s_advance();
|
|
|
|
uint8_t drvid = i2c_led_q[i2c_led_q_s];
|
2019-08-30 18:19:03 +00:00
|
|
|
# if I2C_LED_USE_DMA == 1
|
2018-08-29 19:07:52 +00:00
|
|
|
i2c_led_send_onoff_dma(drvid);
|
2019-08-30 18:19:03 +00:00
|
|
|
# else
|
|
|
|
i2c_led_send_onoff(drvid);
|
|
|
|
# endif
|
2018-08-29 19:07:52 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
i2c_led_q_s_advance(); // Advance last run command or if the command byte was not serviced
|
2018-08-29 19:07:52 +00:00
|
|
|
|
2019-08-30 18:19:03 +00:00
|
|
|
# if I2C_LED_USE_DMA != 1
|
2018-08-29 19:07:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
i2c_led_q_running = 0;
|
2019-08-30 18:19:03 +00:00
|
|
|
# endif
|
2018-08-29 19:07:52 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2019-08-30 18:19:03 +00:00
|
|
|
#endif // !defined(MD_BOOTLOADER) && defined(RGB_MATRIX_ENABLE)
|