|
|
爱科技、爱创意、爱折腾、爱极致,我们都是技术控
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 zzz 于 2013-6-29 17:08 编辑
现在市面上有很多很多的LED显示屏,但大多都基于同一种,或者同一类型的驱动方式,简称08驱动方式,它内部有LED点阵,74HC595,以及138行扫芯片组成,但毫无例外的,是主控板都采用了STC方案,或者串口方案,而Arduino驱动却鲜有人用,LED业界标准Arduino驱动方案的介绍与解析 | 重点便是行驱的模式,今天我推荐两种驱动模式,分别是16*16驱动模式,和16*64直接驱动模式,这两种模式各有特点,16*16即是汉字是一个一个取模,一个一个显示,而16*64是取模软件直接取出一幅完整的图像。如果是显示汉字的同学,推荐采用第一种,而显示图形的同学,推荐采用第二种。
下面,先把主体的演示代码发一下。
- #define RowA 2
- #define RowB 3
- #define RowC 4
- #define RowD 5
- //业界所采用的ABCD 08驱动模式
- int hc138en=6; //EN口
- //使用了硬件SPI,以下脚不能更改
- #define R1 11 //数据出 MOSI
- #define CLK 13 //时钟 SCK
- #define STB 10 //595 刷新显示 SS
- #define DATAIN 12 //数据入,读FLASH时有用 MISO
- byte row=0;
- byte zzz[] =
- {
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF1,0xFF,0xE3,0xFF,0xC0,0x00,0x00,
- 0xFF,0xF1,0xFF,0xE3,0xFF,0xC0,0x00,0x00,0x00,0x70,0x00,0xE0,0x01,0xC0,0x00,0x00,
- 0x00,0xE0,0x01,0xC0,0x03,0x80,0x00,0x00,0x01,0xC0,0x03,0x80,0x07,0x00,0x00,0x00,
- 0x03,0x80,0x07,0x00,0x0E,0x00,0x00,0x00,0x07,0x00,0x0E,0x00,0x1C,0x00,0x00,0x00,
- 0x0E,0x00,0x1C,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0x38,0x00,0x70,0x00,0x00,0x00,
- 0x38,0x00,0x70,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0xE0,0x01,0xC0,0x00,0x00,0x00,
- 0xE0,0x01,0xC0,0x03,0x80,0x00,0x00,0x00,0xFF,0xF1,0xFF,0xE3,0xFF,0xC0,0x00,0x00,
- 0xFF,0xF1,0xFF,0xE3,0xFF,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
- };
- void spi_transfer(volatile char data)
- {
- SPDR = data; // Start the transmission
- while (!(SPSR & (1<<SPIF))) // Wait the end of the transmission
- {
- };
- //return SPDR; // return the received byte
- }
- void hc138sacn(byte r){ //输出行线状态ABCD (A低,D高)
- digitalWrite(RowA,(r & 0x01));
- digitalWrite(RowB,(r & 0x02));
- digitalWrite(RowC,(r & 0x04));
- digitalWrite(RowD,(r & 0x08));
- }
-
- void hc595senddata(byte data){// 高位在前 反相(1亮0灭)
- for (byte i=0; i<8;i++) {
- digitalWrite(CLK,0);
- if (data & 0x80) {
- digitalWrite(R1, 0);
- } else {
- digitalWrite(R1, 1);
- }
- data=data<<1;
- digitalWrite(CLK,1);
- }
-
- }
-
-
- void setup () {
- pinMode(RowA, OUTPUT);
- pinMode(RowB, OUTPUT);
- pinMode(RowC, OUTPUT);
- pinMode(RowD, OUTPUT); //138片选
- pinMode(hc138en, OUTPUT); //138 使能
-
- pinMode(R1, OUTPUT);//595 数据
- pinMode(CLK, OUTPUT); //595 时钟
- pinMode(STB, OUTPUT); //595 使能
- pinMode(DATAIN, INPUT); //595 使能
-
- //digitalWrite(hc138d, HIGH);
- Serial.begin(19200);
-
- //SPI硬件设置
- // SPCR = 01010000
- //interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
- //sample on leading edge of clk,system clock/4 rate (fastest)
- SPCR = (1<<SPE)|(1<<MSTR);
- delay(10);
- }
-
- void loop () {
- if (Serial.available() > 0) {
- // read the incoming byte:
- row = Serial.read();
- // say what you got:
- Serial.print("I received: ");
- Serial.println(row, DEC);
- }
- for(row=0;row<16;row++){
- for (int i=0;i<8;i++){
- spi_transfer(~(zzz[i+row*8]));
- }
-
- digitalWrite(hc138en, 1); //关闭显示
- hc138sacn(row); //换行
- digitalWrite(STB, 0); //595刷新
- digitalWrite(STB, 1);
- delayMicroseconds(500) ; //节电用,
- digitalWrite(hc138en, 0); //开启显示
- delayMicroseconds(500) ; //刷新频率调,差不多60HZ,1/16间隔
-
- }
- }
复制代码 从上面代码可以轻松的看出驱动原理并不复杂,其实就是把取模软件生成的字模,储存到一个数组里,然后调用arduino的硬件SPI通讯接口,把每8个LED看成是一个十六进制编码,然后一行一行的发送出去,发完一行后,再发出换行指令,就是这么简单。
置于16*16单个汉字取模显示,只要把主loop里的代码改为即可,其实就是简单的数学原理,把数组里的十六进制数按照一组一组提取显示而已~
- for(row=0;row<16;row++){
- for (int i=0;i<4;i++){//8片595
- //硬件SPI 发送8字节耗时:22US
- spi_transfer(~(zzz[i*32+row*2])); //硬件SPI
- spi_transfer(~(zzz[i*32+row*2+1])); //硬件SPI
- }
复制代码
取模软件请选择横向顺序扫描~下载在附件哦~

楼上所有的楼主和访客表示感谢,提供了很多有用的资料。
我在此基础上实现了64*32LED阵列的显示,但是问题是用软件SPI刷新速度跟不上,屏幕呈闪烁状态。顾想通过软硬SPI一起执行,提高速率。但是失败了。用了硬件SPI就没法用软件SPI,用了软件SPI就没法用硬件SPI。(我对SPI通信不是很懂)望各位能不能提供好的方法提高刷新速度。
PS:刷 64 * 16 还算可以,可是64 * 32 就 不行了。
附上代码:
- int R1=11;
- int R2=9;
- int STR=10;
- int CLK=13;
- int EN=6;
-
-
- int dig0=2;
- int dig2=3;
- int dig4=4;
- int dig8=5;
- //int i,m;
-
- unsigned char jj,j;
- int down = 0;
- int row_ = 0;
- void setup()
- {
- pinMode(R1,OUTPUT);
- pinMode(R2,OUTPUT);
- pinMode(STR,OUTPUT);
- pinMode(CLK,OUTPUT);
- pinMode(EN,OUTPUT);
- pinMode(dig0,OUTPUT);
- pinMode(dig2,OUTPUT);
- pinMode(dig4,OUTPUT);
- pinMode(dig8,OUTPUT);
- //Serial.begin(19200);
- //SPCR = (1<<SPE)|(1<<MSTR);
- //delay(10);
- }
-
- unsigned char zi[]={
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFD,0xFE,0x1E,0x01,0xBF,0x7F,0x7F,0x7F,0xFD,0xFD,0xEF,0xFD,0xBE,0xFF,0x7F,0x7F,
- 0xF9,0xFB,0xF7,0xFB,0xBD,0xFF,0x7F,0x7F,0xFA,0xFB,0xFF,0xF7,0xBB,0xFF,0x6F,0x7B,
- 0x87,0x0D,0xFF,0xEF,0xB7,0xFF,0x70,0x07,0xEF,0xBE,0x1F,0xDF,0xAB,0xFF,0x7C,0x1F,
- 0xF7,0x7F,0xEF,0xDF,0x9D,0xFF,0x7E,0x3F,0xF7,0x7F,0xF7,0xBF,0xBD,0xFF,0x7C,0x1F,
- 0xF0,0x7B,0xF7,0x7F,0xBE,0xEF,0x7D,0xDF,0xF7,0x7D,0xEE,0xFF,0xBF,0x6F,0x7B,0xEF,
- 0xEF,0xBE,0x1E,0x01,0xBF,0xB0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xED,0xB7,0xFE,0xFF,0xFD,0xFF,0x90,0x07,0xE5,0x37,0xFF,0x7F,0xFE,0xFF,0xDF,0xF7,
- 0xF5,0xE0,0x40,0x01,0x80,0x03,0xFF,0xF7,0xC8,0x2F,0xDF,0xFD,0xBF,0xFB,0x60,0x37,
- 0xF1,0xCE,0xF8,0x07,0xF3,0x3F,0x6F,0xB7,0xCC,0x4E,0xFF,0xCF,0xE7,0xCF,0x6F,0xB7,
- 0xFB,0xF5,0xFF,0xBF,0x9F,0xE3,0x60,0x37,0xC0,0x75,0xFF,0x7F,0x3F,0xF9,0x6F,0xB7,
- 0xEE,0xF9,0x80,0x00,0xC0,0x07,0x6F,0xB7,0xEE,0xFB,0xFF,0x7F,0xFE,0xFF,0x60,0x37,
- 0xF1,0xF5,0xFF,0x7F,0xFE,0xFF,0x6F,0xF7,0xF0,0x4C,0xFF,0x7F,0xFE,0xFF,0x7F,0xF7,
- 0xCE,0x9F,0x78,0x7F,0x80,0x03,0x7F,0x87,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
- };
-
- void row1(int i)
- {
- digitalWrite(dig0,(i&0x01));
- digitalWrite(dig2,(i&0x02));
- digitalWrite(dig4,(i&0x04));
- digitalWrite(dig8,(i&0x08));
- }
-
- void spi_transfer(volatile char data)
- {
- SPDR = data; // Start the transmission
- while (!(SPSR & (1<<SPIF))) // Wait the end of the transmission
- {
- };
- //return SPDR; // return the received byte
- }
- void sendbyte(byte bbyte1){// 高位在前 反相(1亮0灭)
- for (byte i=0; i<8;i++) {
- digitalWrite(CLK,0);
- if ( bbyte1 & 0x80) {
- digitalWrite(R1, 1);
- } else {
- digitalWrite(R1, 0);
- }
- bbyte1= bbyte1<<1;
- digitalWrite(CLK,1);
- }
- }
- void sendbyte2(byte bbyte1){// 高位在前 反相(1亮0灭)
- for (byte i=0; i<8;i++) {
- digitalWrite(CLK,0);
- if ( bbyte1 & 0x80) {
- digitalWrite(R2, 1);
- } else {
- digitalWrite(R2, 0);
- }
- bbyte1= bbyte1<<1;
- digitalWrite(CLK,1);
- }
- }
-
- void loop()
- {
- unsigned char row,m,k;
- for(row=0;row<16;row++){
-
- for (int i=0;i<8;i++){
- sendbyte(zi[i+down*8]);
- }
- // for(row=0;row<16;row++){
- //
- // for (int i=0;i<8;i++){
- // //spi_transfer((zi[i+down*8]));
- // }
-
- down++;
- digitalWrite(EN,1);
- row1(row);
- digitalWrite(STR,0);
- digitalWrite(STR,1);
- delayMicroseconds(0);
- digitalWrite(EN,0);
- delayMicroseconds(0);
- }
-
- for(row=0;row<16;row++){
-
- for (int i=0;i<8;i++){
- sendbyte2(zi[i+down*8]);
- }
- down++;
- digitalWrite(EN,1);
- row1(row);
- digitalWrite(STR,0);
- digitalWrite(STR,1);
- delayMicroseconds(0);
- digitalWrite(EN,0);
- delayMicroseconds(0);
- }
- down = 0;
- }
复制代码 还有一段在uno上能正常跑起来的滚屏
- #define RowA 2
- #define RowB 3
- #define RowC 4
- #define RowD 5
- //业界所采用的ABCD 08驱动模式
- /* 2 ABCD G1 G2 STB CLK 16 1 N N N EN R1 R2 N N 15*/
- int hc138en=6; //EN口
-
- //使用了硬件SPI,以下脚不能更改
- #define R1 11 //数据出 MOSI
- #define CLK 13 //时钟 SCK
- #define STB 10 //595 刷新显示 SS 锁存 STB(LT)为锁存信号,CLK(CK)为时钟信号
- #define DATAIN 12 //数据入,读FLASH时有用 MISO
-
- char yid,h; //YID为移动计数器,H为行段计数器
- int zimuo; //字模计数器
- byte Zdate[10]; //缓存
- byte zzz[] =
- {/*------------------------------------------------------------------------------
- ; 源文件 / 文字 : 吉祥如意
- ; 宽×高(像素): 64×16
- ------------------------------------------------------------------------------*/
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x01,0x00,0x01,0x04,0xFF,0xFE,0x01,0x00,0x01,0x00,0x01,0x08,0x7F,0xFC,0x00,0x00,
- 0x00,0x00,0x1F,0xF0,0x10,0x10,0x10,0x10,0x10,0x10,0x1F,0xF0,0x10,0x10,0x00,0x00,/*"吉",0*/
- 0x21,0x04,0x10,0x88,0x10,0x50,0xFD,0xFE,0x04,0x20,0x08,0x20,0x11,0xFC,0x38,0x20,
- 0x54,0x20,0x94,0x20,0x13,0xFE,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0x20,/*"祥",1*/
- 0x10,0x00,0x10,0x00,0x18,0x00,0x10,0x7E,0xFE,0x42,0x22,0x42,0x22,0x42,0x22,0x42,
- 0x22,0x42,0x24,0x42,0x14,0x42,0x08,0x42,0x14,0x7E,0x23,0x42,0x42,0x42,0x80,0x00,/*"如",2*/
- 0x01,0x00,0x3F,0xFC,0x08,0x20,0x04,0x40,0xFF,0xFE,0x00,0x00,0x1F,0xF0,0x12,0x10,
- 0x11,0x10,0x1F,0xF0,0x00,0x00,0x29,0x08,0x28,0x84,0x68,0x14,0x07,0xF0,0x00,0x00,/*"意",3*/
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- };
- void spi_transfer(volatile char data)
- {
- SPDR = data; // Start the transmission
- while (!(SPSR & (1<<SPIF))) // Wait the end of the transmission
- {
- };
- //return SPDR; // return the received byte
- }
-
-
- void hc138sacn(byte r){ //输出行线状态ABCD (A低,D高)
- digitalWrite(RowA,(r & 0x01));
- digitalWrite(RowB,(r & 0x02));
- digitalWrite(RowC,(r & 0x04));
- digitalWrite(RowD,(r & 0x08));
- }
- /**************************************************************************************
- 左移显示数据生成模块:(功能相当于有返回值的函数 )
- ***************************************************************************************/
- char two_onebyteL(char h1,char h2)
- {
- char temp,tempcol; //输出变量;列移动位数变量。
- if(yid<8)
- tempcol=yid;
- else
- tempcol=yid-8;
- temp=(h1<<tempcol)|(h2>>(8-tempcol)); //左移显示
- temp=255-temp;
- return temp; //将显示数据返回显示输出函数。
- }
- /*************************************************************************************
- 左移待显示数据调取函数
- *************************************************************************************/
- void loadoneline_L(void)
- {
- char s; //此处不要用uchar定义s
- for(s=4;s>=0;s--) //s值为屏数加1(16*16为一屏)
- {
- Zdate[2*s]=zzz[zimuo+32*s+2*h];
- Zdate[2*s+1]=zzz[zimuo+1+32*s+2*h]; //左移显示
- }
- }
- /**********************************************************************************
- 左移显示数据输出函数 : 为显示数据生成模块的h1、h2赋值并且输出合成后的新的h1、h2数据
- ***********************************************************************************/
- void sendoneline_L(void)
- {
- char s;char inc;
- if(yid<8)inc=0;else inc=1;
- for(s=0+inc;s<8+inc;s++)
- { //左移显示:单屏s=1+,4屏s=7+,8屏s=15+;
- spi_transfer(two_onebyteL(Zdate[s],Zdate[s+1]));
- }
- }
- int uiAllData = sizeof(zzz)-128; //定义显示字幕数据的长度 2048/32=64
- void setup ()
- {
- pinMode(RowA, OUTPUT);
- pinMode(RowB, OUTPUT);
- pinMode(RowC, OUTPUT);
- pinMode(RowD, OUTPUT); //138片选
- pinMode(hc138en, OUTPUT); //138 使能
- pinMode(R1, OUTPUT);//595 数据
- pinMode(CLK, OUTPUT); //595 时钟
- pinMode(STB, OUTPUT); //595 使能
- pinMode(DATAIN, INPUT); //595 使能
- //digitalWrite(hc138d, HIGH);
- Serial.begin(19200);
- SPCR = (1<<SPE)|(1<<MSTR);
- delay(10);
- }
-
- void loop ()
- {
- char i,d=10;
- yid=0;
- h=0;
- zimuo=0;
- while(1)
- {
- while(yid<16) //数据移位。
- {
- for(i=0;i<d;i++) //移动速度
- {
- for(h=0;h<16;h++)
- {
- loadoneline_L();
- sendoneline_L();
-
- digitalWrite(hc138en, 1); //关闭显示
- digitalWrite(STB, 0); //595刷新
- digitalWrite(STB, 1);
- hc138sacn(h); //换行
- delayMicroseconds(50) ; //节电用,
- digitalWrite(hc138en, 0); //开启显示
- delayMicroseconds(500) ; //刷新频率调,差不多60HZ,1/16间隔
- }
- }
- yid++; //移动一步
- }
- yid=0;
- zimuo=zimuo+32; //后移一个字,
- if(zimuo>= uiAllData ) //定义显示字幕数据的长度 2048/32=64 即显示完64个16*16大小的字符后 重新循环
- zimuo=0;
- }
- }
复制代码
|
|