March 31, 2012
by amartinez
|
Anyone, I'm trying to pick this code apart and I think the problem is with the column driver but I dare not touch the code because I have no idea what I'm doing with it as of yet.
The LED marquee scrolls fine but the refresh rate is slow, enough to not be able to read things clearly. Here's the code. It's should scroll a simple message on your LED Array. You'll see the refresh rate very slow. I noticed watching the videos that the refresh rate on the given marquees is rather quick and hardly noticeable. Not so with mine. It's very choppy, slow and blinks almost one time per second.
If you copy this code you will have to make sure your header file directory paths reflect your existing file structure.
/ ledarray.c
// ATmega168
// 24 X 5 LED matrix
#include "../avr/include/stdio.h"
#include "../avr/include/avr/io.h"
#include "../avr/include/avr/interrupt.h"
#include "../avr/include/avr/pgmspace.h"
#include "../avr/include/inttypes.h"
#include "../libnerdkits/delay.h"
#include "../libnerdkits/uart.h"
#include "font.h"
// PIN DEFINITIONS:
//
// PB1-5 ROW DRIVERS (0-4)
// PC0-5,PD2-7: COLUMN DRIVERS (0-11)
#define F_CPU 14745600
#define ROWS 5
#define COLS 24
volatile uint8_t la_row, real_row;
volatile uint8_t la_data[COLS];
inline uint8_t ledarray_get(uint8_t i, uint8_t j) {
if(i < ROWS && j < COLS) {
if((la_data[j] & (1<<i)) != 0) {
return 1;
} else {
return 0;
}
} else {
return 0;
}
}
inline void ledarray_set(uint8_t i, uint8_t j, uint8_t onoff) {
if(i < ROWS && j < COLS) {
if(onoff) {
la_data[j] |= (1<<i);
} else {
la_data[j] &= ~(1<<i);
}
}
}
inline void ledarray_set_columndriver(uint8_t j, uint8_t onoff, uint8_t sense) {
// cols 0-5: PC0-5
// cols 6-11: PD2-7
if(j < 6) {
if(onoff) {
PORTC |= (1 << (PC0 + j));
} else {
PORTC &= ~(1<< (PC0 + j));
}
if(sense == onoff) {
DDRC |= (1 << (PC0 + j));
} else {
DDRC &= ~(1 << (PC0 + j));
PORTC &= ~(1 << (PC0 + j));
}
} else {
if(onoff) {
PORTD |= (1 << (PD2 + (j-6)));
} else {
PORTD &= ~(1<< (PD2 + (j-6)));
}
if(sense == onoff) {
DDRD |= (1 << (PD2 + (j-6)));
} else {
DDRD &= ~(1 << (PD2 + (j-6)));
PORTD &= ~(1 << (PD2 + (j-6)));
}
}
}
inline void ledarray_all_off() {
// turn off all row drivers
DDRB &= ~( (1<<PB1)|(1<<PB2)|(1<<PB3)|(1<<PB4)|(1<<PB5) );
PORTB &= ~( (1<<PB1)|(1<<PB2)|(1<<PB3)|(1<<PB4)|(1<<PB5) );
// turn off all column drivers
DDRC &= ~( (1<<PC0) | (1<<PC1) | (1<<PC2) | (1<<PC3) | (1<<PC4) | (1<<PC5) );
PORTC &= ~( (1<<PC0) | (1<<PC1) | (1<<PC2) | (1<<PC3) | (1<<PC4) | (1<<PC5) );
DDRD &= ~( (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD6) | (1<<PD7) );
PORTD &= ~( (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD6) | (1<<PD7) );
}
ISR(TIMER0_OVF_vect) {
// turn off old row driver
DDRB &= ~(1 << (PB1 + real_row));
PORTB &= ~(1 << (PB1 + real_row));
ledarray_all_off();
// increment row number
if(++la_row == 2*ROWS)
la_row = 0;
// set column drivers appropriately
uint8_t j;
if(la_row%2 == 0) {
// even la_row number: fill even columns
real_row = la_row / 2;
for(j=0; j<COLS/2; j++) {
ledarray_set_columndriver(j, ledarray_get(real_row, 2*j), 1);
}
// activate row driver SINK
PORTB &= ~(1 << (PB1 + real_row));
DDRB |= (1 << (PB1 + real_row));
} else {
// odd la_row number: fill odd columns
real_row = (la_row-1)/2;
for(j=0; j<COLS/2; j++) {
ledarray_set_columndriver(j, 1 - ledarray_get(real_row, 2*j + 1), 0);
}
// activate row driver SOURCE
PORTB |= (1 << (PB1 + real_row));
DDRB |= (1 << (PB1 + real_row));
}
}
void ledarray_init() {
// Timer0 CK/64 (900Hz)
TCCR0B = (1<<CS01) | (1<<CS00);
TIMSK0 = (1<<TOIE0);
// outputs (set row drivers high for off)
DDRC &= ~( (1<<PC0) | (1<<PC1) | (1<<PC2) | (1<<PC3) | (1<<PC4) | (1<<PC5) );
DDRD &= ~( (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD6) | (1<<PD7) );
DDRB &= ~( (1<<PB1)|(1<<PB2)|(1<<PB3)|(1<<PB4)|(1<<PB5) );
}
void ledarray_left_shift() {
// shift everything one position left
uint8_t i, j;
for(i=0; i<ROWS; i++) {
for(j=0; j<COLS-1; j++) {
ledarray_set(i,j, ledarray_get(i, j+1));
}
ledarray_set(i,23,0);
}
}
void ledarray_right_shift(){
// shift everything right one position
uint8_t i, j;
for(i=0; i<ROWS; i++) {
for(j=COLS-1; j>0; j--) {
ledarray_set(i,j, ledarray_get(i, j-1));
}
ledarray_set(i,0,0);
}
}
void ledarray_shift_up(){
// shift everything up one position
uint8_t i,j;
for(i=0;i<ROWS-1;i++){
for(j=0;j<COLS;j++){
ledarray_set(i,j,ledarray_get(i+1,j));
}
}
// blank bottom line to prevent 'smearing'
for(j=0;j<COLS;j++){
ledarray_set(4,j,0);
}
}
void ledarray_shift_down(){
// shift everything down one position
uint8_t i,j;
for(i=4;i>0;i--){
for(j=0;j<COLS;j++){
ledarray_set(i,j,ledarray_get(i-1,j));
}
}
// blank top line to prevent 'smearing'
for(j=0;j<COLS;j++){
ledarray_set(0,j,0);
}
}
void ledarray_blank() {
uint8_t i, j;
for(i=0; i<ROWS; i++) {
for(j=0; j<COLS; j++) {
ledarray_set(i,j,0);
}
}
}
void ledarray_testpattern() {
uint8_t i, j, ct;
ledarray_blank();
// set initital pattern (every other LED on)
for(i=0;i<ROWS;i++) {
for(j=i%2;j<COLS;j += 2) {
ledarray_set(i,j, 1 - ledarray_get(i,j));
}
}
//wait 50ms
delay_ms(5);
// toggle the pattern turning what was on off then back on again 10 times
for(ct=0;ct<20;ct++){
for(i=0;i<ROWS;i++) {
for(j=0;j<COLS;j++) {
ledarray_set(i,j, 1 - ledarray_get(i,j));
}
}
delay_ms(5);
for(i=0;i<ROWS;i++) {
for(j=0;j<COLS;j++){
ledarray_set(i,j, 1 - ledarray_get(i,j));
}
}
delay_ms(5);
}
}
void font_get(char match, char *buf) {
// copies the character "match" into the buffer
uint8_t i;
PGM_P p;
for(i=0; i<FONT_SIZE; i++) {
memcpy_P(&p, &font[i], sizeof(PGM_P));
if(memcmp_P(&match, p,1)==0) {
memcpy_P(buf, p, 7);
return;
}
}
// NO MATCH?
font_get('?', buf);
}
uint8_t font_width(char c) {
char buf[7];
buf[1] = 0;
font_get(c, buf);
return buf[1];
}
void font_display(char c, uint8_t offset) {
char buf[7];
font_get(c, buf);
uint8_t width = buf[1];
uint8_t i, j;
for(i=0; i<ROWS; i++) {
for(j=0; j<width; j++) {
if((offset + j) < COLS) {
if( (buf[2+j] & (1<<i)) != 0) {
ledarray_set(i,offset + j,1);
} else {
ledarray_set(i,offset + j,0);
}
}
}
}
// blank the next column to the right
for(i=0; i<ROWS; i++) {
ledarray_set(i, offset+width, 0);
}
}
void font_display_ud(char c, uint8_t offset, uint8_t up) {
char buf[7];
// get font data for the current character and place it into the buffer
font_get(c, buf);
uint8_t width = buf[1];
uint8_t i, j, s,n;
if(up){
for(i=0; i<ROWS; i++) {
n=0;
s=4-i;
while(s<ROWS){
for(j=0; j<width; j++) {
if((offset + j) < COLS) {
if( (buf[2+j] & (1<<n)) != 0) {
ledarray_set(s,offset + j,1);
} else {
ledarray_set(s,offset + j,0);
}
}
}
delay_ms(2);
// AL... THE DELAY HERE WAS 7 I CHANGED IT TO 2
n++;
s++;
}
}
} else{
for(i=0; i<ROWS; i++) {
n=4-i;
s=0;
while(s<=i){
for(j=0; j<width; j++) {
if((offset + j) < COLS) {
if( (buf[2+j] & (1<<n)) != 0) {
ledarray_set(s,offset + j,1);
} else {
ledarray_set(s,offset + j,0);
}
}
}
delay_ms(7);
n++;
s++;
}
}
}
// blank the next column to the right
for(i=0; i<ROWS; i++) {
ledarray_set(i, offset+width, 0);
}
}
void scroll_display(const char *s,uint8_t speed) {
// clear display
ledarray_blank();
int8_t offset = 0 , next_offset = 0;
uint8_t is_started = 0;
uint8_t i=0;
char x=' ';
if(speed==0)
speed=40;
// begin loop and continue until nul character at end of string
while(pgm_read_byte(s) != 0x00){
// have we read a character?
if(is_started) {
// if so, place and shift the character until it is clear of column 24
while(next_offset>23){
delay_ms(speed);
// shift the display one place
ledarray_left_shift();
// check the end of the currently displayed characters if it's greater than zero, decrement
// both the offset (character display position), and next_offset(character end position)
if(next_offset > 0) {
offset -= 1;
next_offset -= 1;
}
// display the character at the new position
font_display(x, offset);
}
} else {
// if not, set offset to # of columns -1 (23)
offset = COLS-1;
}
// read the next character in the string
x = pgm_read_byte(s++);
// if we have already started, set the current display position to where the last character ended
if(is_started)
offset = next_offset;
// display the character
font_display(x, offset);
// create the new character end position
next_offset = offset + font_width(x)+1;
// set flag to show we have been through the loop
is_started = 1;
}
// Process the last character. This is neccessary since the while bails as soon as it reads
// the null character. At that point, the last read character has not yet shifted into full
// view. The following while loop shifts the final string character into full view.
while(next_offset>23){
delay_ms(speed);
ledarray_left_shift();
if(next_offset > 0) {
offset -= 1;
next_offset -= 1;
}
font_display(x, offset);
}
delay_ms(speed);
// this final loop shifts the display all the way off.
for(i=0;i<COLS;i++){
ledarray_left_shift();
delay_ms(speed);
}
return;
}
void do_testpattern() {
int8_t offset=0;
int8_t i=0;
while(1) {
// Simple scrolling message
ledarray_blank();
scroll_display(PSTR("BAD REFRESH RATE"),10);
ledarray_blank();
}
return;
}
// }
int main() {
ledarray_init();
// activate interrupts
sei();
/*
// init serial port
uart_init();
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
stdin = stdout = &uart_stream;
*/
// do test pattern has been re-written
do_testpattern();
return 0;
}
|