rgbyteclock-code/led1642gw.c

250 lines
5.7 KiB
C

/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <struppi@struppi.name> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
* (c) 2014 Stefan Rupp
* ----------------------------------------------------------------------------
*/
#include <string.h>
#include "led1642gw.h"
#include "led1642gw_config.h"
#define NUM_LED1642GW_CHANNELS (16) // number of LED channels per IC
//total numer of channels. needed to calculate the buffer size.
#define NUM_LED_CHANNELS (NUM_LED1642GW_CHANNELS*NUM_LED1642GW_ICs)
/* The buffer to hold the LED values.
* The data in this buffer can be manipulated with
* e.g. led1642gw_set().
* calling led1642gw_flush() sends the data in this buffer
* the data registers of the LED1642 ICs.
*/
static uint16_t ledbuffer[NUM_LED_CHANNELS];
static uint16_t config_reg[NUM_LED1642GW_ICs];
/*
* Write 16 bits of \data, with LE set high
* for the number of clock cycles specified in \le_clocks.
* MSB comes first, LSB is last.
*/
static void write_data(uint16_t data, uint8_t le_clocks)
{
uint16_t mask = 0x8000;
int8_t bit;
SET_CLK_L();
SET_LE_L();
for (bit=15; bit>=le_clocks; bit--) {
if(data&mask) { SET_SDI_H(); }
else { SET_SDI_L(); }
SET_CLK_H();
mask >>= 1;
SET_CLK_L();
}
SET_LE_H();
for (/*noting to initialize*/; bit>=0; bit--) {
if(data&mask) { SET_SDI_H(); }
else { SET_SDI_L(); }
SET_CLK_H();
mask >>= 1;
SET_CLK_L();
}
// set all pins to low after transmission
//SET_CLK_L();
SET_LE_L();
SET_SDI_L();
}
/*
* Write data to BRIGHTNESS DATA LATCH register.
* that means setting LE high for 3 or 4 clock cycles
*/
static void write_data_latch(uint16_t data)
{
write_data(data, 4);
}
/*
* Write data to BRIGHTNESS GLOBAL LATCH register.
* that means setting LE high for 5 or 6 clock cycles
*/
static void write_global_latch(uint16_t data)
{
write_data(data, 6);
}
/*
* This function shifts data through the 16bit shift
* register of the LED1642GW, without writing the data
* to any internal register of the IC.
* This way, we can daisy chain an bunch of LED1642GW ICs,
* and still get data through to any of those.
*/
static void write_no_command(uint16_t data)
{
write_data(data, 0);
}
/*
* Write data to CONFIG register.
* that means setting LE high for 7 clock cycles
*/
void led1642gw_flush_config()
{
uint8_t ic;
for (ic=0; ic<(NUM_LED1642GW_ICs-1); ic++) {
write_no_command(config_reg[ic]);
}
write_data(config_reg[ic], 7);
}
/*
* Turn all channels on, so the data in the DATA LATCH
* register affects the LEDs attached to the IC.
*/
void led1642gw_turn_all_on(void)
{
uint8_t ic;
for (ic=0; ic<(NUM_LED1642GW_ICs-1); ic++) {
write_no_command(0xffff);
}
write_data(0xffff, 2);
}
/*
* Turn all channels off,
*/
void led1642gw_turn_all_off(void)
{
uint8_t ic;
for (ic=0; ic<(NUM_LED1642GW_ICs-1); ic++) {
write_no_command(0x0000);
}
write_data(0x0000, 2);
}
void led1642gw_set_gain(uint8_t gain)
{
if (gain > 0x3f) {
gain = 0x3f;
}
uint16_t g = gain;
for (uint8_t ic=0; ic<(NUM_LED1642GW_ICs); ic++) {
config_reg[ic] &= ~(0x003f);
config_reg[ic] |= g;
}
}
void led1642gw_set_current_mode(uint8_t mode)
{
uint16_t mask = (1<<6);
for (uint8_t ic=0; ic<(NUM_LED1642GW_ICs); ic++) {
if (mode) { config_reg[ic] |= mask; }
else { config_reg[ic] &= ~mask; }
}
}
/*
* Initialize the pins of the ATMega processor
* to drive the data signals to the ICs
* and initialize the LED buffer to zero.
*/
void led1642gw_init(void)
{
SET_CLK_L();
SET_SDI_L();
SET_LE_L();
DDR_CLK |= (1<<PIN_CLK);
DDR_SDI |= (1<<PIN_SDI);
DDR_LE |= (1<<PIN_LE);
memset(ledbuffer, 0x00, sizeof(ledbuffer));
memset(config_reg, 0x00, sizeof(config_reg));
led1642gw_flush();
}
/*
* Transmit data from the ledbuffer to the BRIGHTNESS latches of
* the LED driver ICs.
* Let's assume, we have n LED1642GW ICs daisy chained. Then
* we write n-1 times with write_no_command, to shift all
* data through the 16bit shift registers of each of the ICs.
* Then we once write with write_data_latch to store the data
* in the BRIGHTNESS DATA registers of the respective ICs.
* We do this for all but the last set of brightness data,
* where we don't write to the DATA LATCH, but to the GLOBAL DATA LATCH.
*/
void led1642gw_flush(void)
{
uint8_t channel;
uint8_t ic;
// for each of the first 15 channels, do the following:
for (channel=0; channel<NUM_LED1642GW_CHANNELS-1; channel++) {
// shift data throught the first n-1 ICs with write_no_command
for (ic=0; ic<(NUM_LED1642GW_ICs-1); ic++) {
write_no_command(ledbuffer[channel+(NUM_LED1642GW_CHANNELS*ic)]);
}
// then, when the brightness data has propagated through the
// shift registers, write all data into the DATA LATCH of
// all of the ICs.
write_data_latch(ledbuffer[channel+(NUM_LED1642GW_CHANNELS*ic)]);
}
// for the 16th channel, we don't write to the DATA LATCH, but
// to the CLOBAL data latch.
// once more, we do the trick with write_no_command, to
// shift data through all the ICs
for (ic=1; ic<NUM_LED1642GW_ICs; ic++) {
write_no_command(ledbuffer[(ic*NUM_LED1642GW_CHANNELS)-1]);
}
// than, at last, write data to the global latch, to force
// the ICs to update their brightness data from the DATA LATCHES.
write_global_latch(ledbuffer[(ic*NUM_LED1642GW_CHANNELS)-1]);
}
void led1642gw_set_channel(uint8_t channel, uint16_t value)
{
if (channel < NUM_LED_CHANNELS) {
ledbuffer[channel] = value;
}
}
void led1642gw_clear(void)
{
memset(ledbuffer, 0x00, sizeof(ledbuffer));
}