243 lines
5.7 KiB
C
243 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_LE_L();
|
|
for (bit=15; bit>=le_clocks; bit--) {
|
|
SET_CLK_L();
|
|
if(data&mask) { SET_SDI_H(); }
|
|
else { SET_SDI_L(); }
|
|
SET_CLK_H();
|
|
mask >>= 1;
|
|
}
|
|
|
|
SET_LE_H();
|
|
for (/*noting to initialize*/; bit>=0; bit--) {
|
|
SET_CLK_L();
|
|
if(data&mask) { SET_SDI_H(); }
|
|
else { SET_SDI_L(); }
|
|
SET_CLK_H();
|
|
mask >>= 1;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
for (uint8_t ic=0; ic<(NUM_LED1642GW_ICs-1); ic++) {
|
|
config_reg[ic] &= ~(0x003f);
|
|
config_reg[ic] |= gain;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void led1642gw_set_current_mode(uint8_t mode)
|
|
{
|
|
for (uint8_t ic=0; ic<(NUM_LED1642GW_ICs-1); ic++) {
|
|
if (mode) { config_reg[ic] |= (1<<6); }
|
|
else { config_reg[ic] &= ~(1<<6); }
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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+(ic*NUM_LED1642GW_CHANNELS)]);
|
|
}
|
|
// 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));
|
|
}
|
|
|
|
|