main:
//#include <Ticker.h>
#include "pins.h"
#include "msg.h"
typedef uint8_t Pin;
#define PIN_NONE 255
#define ANALOG_OUT_MAX 1023 // for Wemos D1 mini
bool msg_get_state(uint8_t row, uint16_t col){
if(col >= COLS_CNT) col -= COLS_CNT; // wraparound
return msg[row * COLS_CNT + col];
}
uint8_t bit_set(uint8_t x, uint8_t n, bool val = true){return val ? (x | (1 << n)) : (x & ~(1 << n));}
bool bit_get(uint8_t x, uint8_t n){return (x >> n) & 1;}
struct ShiftReg{
Pin p_data,p_clock,p_latch,p_enable;
bool invert;
ShiftReg(Pin data, Pin latch, Pin clock, Pin enable = PIN_NONE, bool _invert = false){
p_data = data; p_clock = clock; p_latch = latch; p_enable = enable; invert = _invert;
pinMode(p_data, OUTPUT); pinMode(p_clock, OUTPUT); pinMode(p_latch, OUTPUT);
if(p_enable != PIN_NONE) pinMode(p_enable, OUTPUT);
}
void write(bool val){ // // write bit to ic mem
digitalWrite(p_clock, LOW);
digitalWrite(p_data, invert ? (val ? LOW : HIGH) : (val ? HIGH : LOW));
digitalWrite(p_clock, HIGH);
}
void write(uint8_t val){ // write byte to ic mem
for(uint8_t n=7;; n--){
this->write(bit_get(val, n));
if(n==0) break;
}
}
void latch(){ // apply ic mem to output pins
digitalWrite(p_latch, LOW);
digitalWrite(p_latch, HIGH);
}
void duty(float val){ // set duty cycle; 1.0 = 100%
if(p_enable == PIN_NONE) return;
// ic uses negative logic (i.e. when OE is LOW, output is enabled, so we correct via 1-x)
analogWrite(p_enable, round((1.0 - val) * ANALOG_OUT_MAX));
}
};
ShiftReg rows(P_ROW_DATA, P_ROW_LATCH, P_ROW_CLOCK, P_ROW_ENABLE);
ShiftReg cols(P_COL_DATA, P_COL_LATCH, P_COL_CLOCK, PIN_NONE, true);
void test_pattern(){ // to check all the LEDs are working
rows.duty(0.00);
cols.write(uint8_t(255));
cols.write(uint8_t(255));
cols.write(uint8_t(255));
cols.latch();
for(uint8_t row = 0; row < 7; row++){
rows.write(bit_set(0, row));
rows.latch();
rows.duty(0.15);
delay(1000);
rows.duty(0.00);
}
}
#define MULTIPLEX_INTERVAL 2100 // us
#define COL_INTERVAL 120 // scroll speed (frames); (1 frame = 7*MULTIPLEX_INTERVAL us)
unsigned long ts_multiplex_lst;
uint8_t row_cur = 0;
uint16_t col_cur = 0;
uint16_t frame_cnt = 0;
bool states_ready = false;
uint8_t row_states = 0;
uint8_t col_states[3] = {0,0,0};
//Ticker ticker;
void states_gen(){
row_states = bit_set(0, row_cur);
for(uint8_t byte_n=0; byte_n<3; byte_n++){
col_states[byte_n] = 0;
for(uint8_t bit_n=0; bit_n<8; bit_n++)
col_states[byte_n] = bit_set(col_states[byte_n], bit_n, msg_get_state(row_cur, col_cur + byte_n*8+bit_n));
}
if(++row_cur == 7){ // 7 would refer to non-existent 8th row
row_cur = 0;
if(++frame_cnt == COL_INTERVAL){ // new frame will be drawn next and we are ready to scroll
frame_cnt = 0;
if(++col_cur == COLS_CNT) col_cur = 0;
}
}
states_ready = true;
}
void states_apply(){
if(!states_ready) return;
//rows.duty(0.00); // disable while feeding data to prevent ghosting
digitalWrite(P_ROW_ENABLE, HIGH);
rows.write(row_states);
cols.write(col_states[2]); // rightmost first
cols.write(col_states[1]);
cols.write(col_states[0]);
rows.latch();
cols.latch();
//rows.duty(0.15);
digitalWrite(P_ROW_ENABLE, LOW);
ts_multiplex_lst = micros();
states_ready = false; // must generate new states before next call
}
void setup(){
analogWriteFreq(40000); //40kHz
test_pattern();
ts_multiplex_lst = micros();
//ticker.attach_ms(MULTIPLEX_INTERVAL/1000, states_apply);
}
void loop(){
if(!states_ready) states_gen();
if(micros() - ts_multiplex_lst >= MULTIPLEX_INTERVAL) states_apply();
}
msg.h:
//"TO ADMIT DEFEAT IS TO BLASPHEME AGAINST THE EMPEROR --- "
bool msg[] = {
1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,1,1,1,0,0,0,1,0,0,0,1,0,0,1,1,1,0,0,1,1,1,1,1,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,0,1,1,1,0,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,0,1,1,1,1,0,0,1,0,0,0,1,0,1,1,1,1,1,0,1,0,0,0,1,0,1,1,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0,1,0,0,1,1,1,1,0,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,0,1,0,0,0,1,0,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,0,1,0,0,0,1,0,1,1,1,1,0,0,1,1,1,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,1,0,0,1,1,0,1,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,1,0,0,1,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,1,1,1,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,1,0,1,1,1,1,0,0,1,0,1,0,1,0,1,1,1,1,0,0,0,0,0,0,1,0,0,0,1,0,1,0,1,1,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,1,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,1,1,1,1,0,0,1,0,1,0,1,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1,0,0,0,1,0,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,0,0,0,0,
0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,1,1,1,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1,0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,1,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,1,0,0,0,0,1,1,1,0,0,0,0,0,0,1,0,0,0,1,0,1,1,1,0,0,0,1,0,0,0,1,0,0,1,1,1,0,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1,0,1,0,0,0,0,0,1,1,1,1,1,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,1,0,0,1,1,1,1,1,0,1,0,0,0,1,0,1,1,1,1,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,1,1,1,1,0,1,0,0,0,1,0,1,1,1,1,1,0,0,0,0,0,1,0,0,0,1,0,0,1,1,1,1,0,1,0,0,0,1,0,0,1,1,1,0,0,1,0,0,0,1,0,1,1,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,0,1,0,0,0,1,0,1,0,0,0,0,0,1,1,1,1,1,0,1,0,0,0,1,0,0,1,1,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
#define COLS_CNT 316
pins.h:
#define P_ROW_ENABLE D0
#define P_ROW_DATA D7
#define P_ROW_CLOCK D5
#define P_ROW_LATCH D6
#define P_COL_DATA D4
#define P_COL_CLOCK D3
#define P_COL_LATCH D2