LED dot matrix scrolling message – 14×5 – source code

I have built various scrolling message badges for instance using the 16F57- 10×5 and 12×5. However, since these chips only have a small FLASH, there is not much flexibility. I don’t really recommend these controllers for a scrolling message.

Recently I have started a new project, using small 1.9mm ready made modules, and a 16F884. It has 4K FLASH which is sufficient (about 1/3 are used), and 256 bytes RAM. However, the max. continuous RAM is only 80 bytes.


edit: I am now using 2 RAM banks (160 bytes).

The font is proportional, most characters are 4 pixels wide, some are 3 only, and a few are 5 pixels wide. The complete bitmap is precomputed, which requires a large RAM buffer.

This time I tried something new- mapping all the IO randomly, and connecting the LED matrix to just any IO lines. For this purpose a number of tables are used. Also the TRIS registers are setup automatically.

It works so far, however, right now the text is not scrolling. However, this is only a minor additional effort. Also it turned out one module is common anode, the other common cathode, so two sets of tables are required. It is actually easy to correct the layout using these tables.

Table 1 is simply the order of the anodes and cathodes according to the datasheet.

The second table contains pointers to PORT variables, on correlation to the pin numbers in table 1, and in ascending order. The third table contains bit masks to set/reset individual bits (actually distributed over random ports). Also there is a table for the TRIS pointers for automatic setup.

16f884 microcontroller

here you can see the SMD microcontroller, mounted on adapter PCB. The modules are simply soldered directly into the adapter board- this works, since random IO is possible via software.

The LED matrix! It works correctly, the bitmap is copied from the display buffer. In order to scroll the complete bitmap, it is just neccessary to slide a window from the scroll bitmap.

The scroll is generated using the font table, and one of the preprogrammed texts from a table. Later, it is also planned to use keys to program a message directly into the chip, and store it into the EEPROM.

based on 16f884 microcontroller

based on 16f884 microcontroller

Here the source code, without the scrolling implemented (it is already partially programmed, and not much of an effort)

 

 

 

 

 

 

 


//
// LED matrix scrolling message
// 16F midrange PICs can use max. 80 bytes RAM area
// Extended midrange can use *more*
//
// Current configuration: PIC 16F884
//
// (c) by Takao 2014 – Hitechworld Software
// If you like this source code + use it,
// why not send some funds to [email protected] (with paypal)?
//
// All rights reserved, no ownership is transfered.
// You can use this source code for evalution.
//
#if defined(__XC)
#include <xc.h>         /* XC8 General Include File */
#elif defined(HI_TECH_C)
#include <htc.h>        /* HiTech General Include File */
#endif
#include <stdint.h>        /* For uint8_t definition */
#include <stdbool.h>       /* For true/false definition */
#include “system.h”        /* System funct/params, like osc/peripheral config */
#include “user.h”          /* User funct/params, such as InitApp */
#include “chrset.h”
#define c_phases 5
unsigned char v_main_phase,v_PORTA,v_PORTB,v_PORTC,v_PORTD,v_PORTE;
unsigned char i;
// pin configuration for the display
const unsigned char anodes[]={8,7,10,3,1};
const unsigned char cathodes[]={12,11,2,9,4,5,6};
const unsigned char cathodes2[]={7,9,4,1,2};
const unsigned char anodes2[]={12,11,10,3,8,5,6};
// IO pin assignment (above pins in ascending order
const unsigned char* const pin_port1[]={
&v_PORTC,&v_PORTC,&v_PORTC,&v_PORTC,\
&v_PORTD,&v_PORTD,&v_PORTD,&v_PORTD,\
&v_PORTD,&v_PORTD,&v_PORTB,&v_PORTB};
// TRIS registers for automatic configuration
const unsigned char* const tris_port1[]={
&TRISC,&TRISC,&TRISC,&TRISC,\
&TRISD,&TRISD,&TRISD,&TRISD,\
&TRISD,&TRISD,&TRISB,&TRISB};
const unsigned char* const pin_port2[]={
&v_PORTD,&v_PORTD,&v_PORTC,&v_PORTC,\
&v_PORTC,&v_PORTC,&v_PORTA,&v_PORTA,\
&v_PORTE,&v_PORTE,&v_PORTE,&v_PORTA};
const unsigned char* const tris_port2[]={
&TRISD,&TRISD,&TRISC,&TRISC,\
&TRISC,&TRISC,&TRISA,&TRISA,\
&TRISE,&TRISE,&TRISE,&TRISA};
// set or reset a bit
const unsigned char const pin_bit_set1[]={
0x80,0x40,0x20,0x10,0x08,0x04,\
0x10,0x20,0x40,0x80,0x01,0x02};
const unsigned char const pin_bit_reset1[]={
0x7f,0xbf,0xdf,0xef,0xf7,0xfb,\
0xef,0xdf,0xbf,0x7f,0xfe,0xfd};
const unsigned char pin_bit_set2[]={
0x02,0x01,0x08,0x04,0x02,0x01,\
0x40,0x80,0x04,0x02,0x01,0x20};
const unsigned char pin_bit_reset2[]={
0xfd,0xfe,0xf7,0xfb,0xfd,0xfe,\
0xbf,0x7f,0xfb,0xfd,0xfe,0xdf};
const unsigned char c_shl[]={1,2,4,8,16};
unsigned char v_sys_flags,v_led_ctr;
const unsigned char msg1[]={‘K’,’A’,’W’,’A’,’S’,’A’,’K’,’I’,’ ‘,0};
const unsigned char* const msg_arr[]={&msg1,&msg2};
#define display_buffer_size 14
unsigned char display_buffer[display_buffer_size];
unsigned char msg_buffer[80];
unsigned char msg_buf_idx;
unsigned char curr_msg_idx;
unsigned char curr_scroll_idx;
#define display_width 14
void configure_tris()
{unsigned char* tris_reg;
for(i=0;i < 12;i++)
{
tris_reg=tris_port1[i];
*tris_reg&=pin_bit_reset1[i];
tris_reg=tris_port2[i];
*tris_reg&=pin_bit_reset2[i];
}
}
unsigned char reloc_ascii(unsigned char c)
{
if(c > 64)return(c-65);
if(c > 47)return(c-22);
if(c==32)return(39);
if(c==45)return(37);
if(c==46)return(38);
if(c==0)return(0xff);
}
void generate_scroll()
{// build the scroll bitmap in RAM from the font data
unsigned char* curr_msg;
unsigned char* font_data;
unsigned char chr,font_data_size,font_data_value;
curr_msg=msg_arr[curr_msg_idx];
reloop:// load a character
chr=reloc_ascii(*curr_msg);
if(chr==0xff)goto rdy;
font_data=alpha_chr[chr]; // first character contains size bits
font_data_size=(*font_data) > > 5;
font_data_value=(*font_data)&0b11111;
for(i=0;i < font_data_size;i++)
{// copy bitmap data from font data to scroll bitmap
msg_buffer[msg_buf_idx]=font_data_value;
msg_buf_idx++;
font_data++;
font_data_value=*font_data;
}
msg_buffer[msg_buf_idx++]=0;
curr_msg++;
goto reloop;
rdy:;
}
void update_display_buffer()
{unsigned char src_start;
src_start=curr_scroll_idx;
for(i=0;i < display_width;i++)
{// scroll the message by incrementing curr_scroll_idx
display_buffer[i]=msg_buffer[src_start++];
if(src_start > msg_buf_idx)
{
src_start=0;
curr_scroll_idx=0;
}
}
}
//void clr_display_buffer()
//{
//for(i=0;i < display_buffer_size;i++)display_buffer[i]=0;
//}
void refresh_line(unsigned char v_phase)
{unsigned char anode,cathode,v_shl;
unsigned char* portio;
for(i=0;i<c_phases;i++)
{// activate one matrix line
anode=anodes[i]-1;
portio=pin_port1[anode];
if(i==v_phase)*portio|=pin_bit_set1[anode];
else *portio&=pin_bit_reset1[anode];
anode=cathodes2[i]-1;
portio=pin_port2[anode];
if(i==v_phase)*portio&=pin_bit_reset2[anode];
else *portio|=pin_bit_set2[anode];
}
v_shl=c_shl[v_phase];
for(i=0;i < (display_width/2);i++)
{// copy data from the display buffer
cathode=cathodes[i]-1;
portio=pin_port1[cathode];
if(display_buffer[i]&v_shl)*portio&=pin_bit_reset1[cathode];
else *portio|=pin_bit_set1[cathode];
// 2 display modules
cathode=anodes2[i]-1;
portio=pin_port2[cathode];
if(display_buffer[i+7]&v_shl)*portio|=pin_bit_set2[cathode];
else *portio&=pin_bit_reset2[cathode];
}
// update hardware ports
PORTA=v_PORTA;
PORTB=v_PORTB;
PORTC=v_PORTC;
PORTD=v_PORTD;
PORTE=v_PORTE;
}
/******************************************************************************/
/* Main Program                                                               */
/******************************************************************************/
void main(void)
{
/* Configure the oscillator for the device */
//ConfigureOscillator();
/* Initialize I/O and Peripherals for application */
InitApp();
configure_tris();
curr_msg_idx=1;
curr_scroll_idx=0;
msg_buf_idx=0;
generate_scroll();
update_display_buffer();
v_sys_flags=0;
v_led_ctr=0;
v_main_phase=0;
while(1)
{
if(v_sys_flags&1)// timer interrupt was raised
{
v_sys_flags&=0xfe;
refresh_line(v_main_phase); // do multiplex refresh
v_main_phase++;if(v_main_phase==c_phases)v_main_phase=0;
v_led_ctr++;if(v_led_ctr==0x50)// blink the LED
{
v_led_ctr=0;
if(v_sys_flags&0x2)
{
v_sys_flags&=0xfd;
PORTBbits.RB6=1;
}else
{
v_sys_flags|=0x02;
PORTBbits.RB6=0;
}
}
}
}
}

The font data

/*
* File:   chrset.h
* Author: Max
*
* Created on 06 February 2014, 23:34
*/
#define chr_a 0b10011110,0b00101,0b00101,0b11110 //4
#define chr_b 0b10011111,0b10101,0b10101,0b01110 //4
#define chr_c 0b10001110,0b10001,0b10001,0b10001 //4
#define chr_d 0b10011111,0b10001,0b10001,0b01110 //4
#define chr_e 0b10011111,0b10101,0b10101,0b10001 //4
#define chr_f 0b10011111,0b00101,0b00101,0b00001 //4
#define chr_g 0b10011111,0b10001,0b10101,0b11100 //4
#define chr_h 0b10011111,0b00100,0b00100,0b11111 //4
#define chr_i 0b01110001,0b11111,0b10001 //3
#define chr_j 0b10011000,0b10001,0b10001,0b11111 //4
#define chr_k 0b10011111,0b00100,0b01010,0b10001 //4
#define chr_l 0b01111111,0b10000,0b10000 //3
#define chr_m 0b10111111,0b00010,0b00100,0b00010,0b11111 //5
#define chr_n 0b10111111,0b00010,0b00100,0b01000,0b11111 //5
#define chr_o 0b10001110,0b10001,0b10001,0b01110 //4
#define chr_p 0b10011111,0b01001,0b01001,0b00110 //4
#define chr_q 0b10101110,0b10001,0b10001,0b01110,0b10000 //5
#define chr_r 0b10011111,0b00101,0b01101,0b10110 //4
#define chr_s 0b10010111,0b10101,0b10101,0b11101 //4
#define chr_t 0b01100001,0b11111,0b00001 //3
#define chr_u 0b10001111,0b10000,0b10000,0b11111 //4
#define chr_v 0b10001111,0b10000,0b01000,0b00111 //4
#define chr_w 0b10101111,0b10000,0b11000,0b10000,0b01111 //5
#define chr_x 0b10110001,0b01010,0b00100,0b01010,0b10001 //5
#define chr_y 0b10010111,0b10100,0b10100,0b01111 //4
#define chr_z 0b10010001,0b11001,0b10101,0b10011 //4
#define chr_0 0b10001110,0b10001,0b10001,0b01110 //4
#define chr_1 0b01110010,0b11111,0b10000 //3
#define chr_2 0b10011101,0b10101,0b10101,0b10111 //4
#define chr_3 0b10010101,0b10101,0b10101,0b01110 //4
#define chr_4 0b01100111,0b00100,0b11111 //3
#define chr_5 0b10010111,0b10101,0b10101,0b01000 //4
#define chr_6 0b10011111,0b10101,0b10101,0b11101 //4
#define chr_7 0b10000001,0b00101,0b11111,0b00100 //4
#define chr_8 0b10001110,0b10101,0b10101,0b01110 //4
#define chr_9 0b10010111,0b10101,0b10101,0b11111 //4
#define chr_dc 0b01100000,0b01010,0b00000 //3
#define chr_sl 0b01100100,0b00100,0b00100 //3
#define chr_pt 0b00110000 //1
#define chr_empty 0b00100000 //1
const char s_chr_a[]={chr_a};
const char s_chr_b[]={chr_b};
const char s_chr_c[]={chr_c};
const char s_chr_d[]={chr_d};
const char s_chr_e[]={chr_e};
const char s_chr_f[]={chr_f};
const char s_chr_g[]={chr_g};
const char s_chr_h[]={chr_h};
const char s_chr_i[]={chr_i};
const char s_chr_j[]={chr_j};
const char s_chr_k[]={chr_k};
const char s_chr_l[]={chr_l};
const char s_chr_m[]={chr_m};
const char s_chr_n[]={chr_n};
const char s_chr_o[]={chr_o};
const char s_chr_p[]={chr_p};
const char s_chr_q[]={chr_q};
const char s_chr_r[]={chr_r};
const char s_chr_s[]={chr_s};
const char s_chr_t[]={chr_t};
const char s_chr_u[]={chr_u};
const char s_chr_v[]={chr_v};
const char s_chr_w[]={chr_w};
const char s_chr_x[]={chr_x};
const char s_chr_y[]={chr_y};
const char s_chr_z[]={chr_z};
const char s_chr_0[]={chr_0};
const char s_chr_1[]={chr_1};
const char s_chr_2[]={chr_2};
const char s_chr_3[]={chr_3};
const char s_chr_4[]={chr_4};
const char s_chr_5[]={chr_5};
const char s_chr_6[]={chr_6};
const char s_chr_7[]={chr_7};
const char s_chr_8[]={chr_8};
const char s_chr_9[]={chr_9};
const char s_chr_dc[]={chr_dc};
const char s_chr_sl[]={chr_sl};
const char s_chr_pt[]={chr_pt};
const char s_chr_empty[]={chr_empty};
const char* const alpha_chr[]  =
{s_chr_a,s_chr_b,s_chr_c,s_chr_d,s_chr_e,s_chr_f,s_chr_g,s_chr_h,\
s_chr_i,s_chr_j,s_chr_k,s_chr_l,s_chr_m,s_chr_n,s_chr_o,s_chr_p,\
s_chr_q,s_chr_r,s_chr_s,s_chr_t,s_chr_u,s_chr_v,s_chr_w,s_chr_x,s_chr_y,s_chr_z,\
s_chr_0,s_chr_1,s_chr_2,s_chr_3,s_chr_4,\
s_chr_5,s_chr_6,s_chr_7,s_chr_8,s_chr_9,\
s_chr_dc,s_chr_sl,s_chr_pt,s_chr_empty};

user.c

/******************************************************************************/
/* Files to Include                                                           */
/******************************************************************************/
#if defined(__XC)
#include <xc.h>         /* XC8 General Include File */
#elif defined(HI_TECH_C)
#include <htc.h>        /* HiTech General Include File */
#endif
#include <stdint.h>         /* For uint8_t definition */
#include <stdbool.h>        /* For true/false definition */
#include “user.h”
/******************************************************************************/
/* User Functions                                                             */
/******************************************************************************/
/* <Initialize variables in user.h and insert code for user algorithms.> */
void InitApp(void)
{
/* TODO Initialize User Ports/Peripherals/Project here */
/* Setup analog functionality and port direction */
/* Initialize peripherals */
/* Enable interrupts */
TMR0IE=1;
GIE=1;
OPTION_REG=0x03;
TRISB=0b10111111;
ANSEL=0;
ANSELH=0;
}

the configuration bits

// PIC16F884 Configuration Bit Settings
// ‘C’ source line config statements
#include <xc.h>
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
// CONFIG1
#pragma config FOSC = INTRC_NOCLKOUT// Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // RE3/MCLR pin function select bit (RE3/MCLR pin function is digital input, MCLR internally tied to VDD)
#pragma config CP = OFF         // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = OFF      // Brown Out Reset Selection bits (BOR disabled)
#pragma config IESO = OFF       // Internal External Switchover bit (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)
#pragma config LVP = OFF        // Low Voltage Programming Enable bit (RB3 pin has digital I/O, HV on MCLR must be used for programming)
// CONFIG2
#pragma config BOR4V = BOR40V   // Brown-out Reset Selection bit (Brown-out Reset set to 4.0V)
#pragma config WRT = OFF        // Flash Program Memory Self Write Enable bits (Write protection off)

the interrupts

/******************************************************************************/
/*Files to Include                                                            */
/******************************************************************************/
#if defined(__XC)
#include <xc.h>         /* XC8 General Include File */
#elif defined(HI_TECH_C)
#include <htc.h>        /* HiTech General Include File */
#endif
#include <stdint.h>         /* For uint8_t definition */
#include <stdbool.h>        /* For true/false definition */
extern unsigned char v_sys_flags;
/******************************************************************************/
/* Interrupt Routines                                                         */
/******************************************************************************/
/* Baseline devices don’t have interrupts. Note that some PIC16’s
* are baseline devices.  Unfortunately the baseline detection macro is
* _PIC12 */
#ifndef _PIC12
void interrupt isr(void)
{
/* This code stub shows general interrupt handling.  Note that these
conditional statements are not handled within 3 seperate if blocks.
Do not use a seperate if block for each interrupt flag to avoid run
time errors. */
if(TMR0IF)
{
TMR0IF=0;
v_sys_flags|=1;
}
}
#endif

to be continued…with the scrolling logic, and code to program a text into the EEPROM.

In order to use a different display, you only need to change the tables! Also you can use any PIC chip- simply change the port names in the tables.

 

 

 

Leave a Reply

Your email address will not be published.

*