AI8051U试验箱数码管驱动学习笔记,自己实现I2C驱动OLED
官方使用的是SPI驱动的SSD1306的OLED屏幕示例,于是我琢磨了一下,参考I2C驱动EEPROM的示例写基础函数, 一次点亮了屏幕
-
- /************* 功能说明 **************
-
- 本例程基于AI8051U为主控芯片的实验箱V1.1版本进行编写测试。
-
- 使用Keil C251编译器,Memory Model推荐设置XSmall模式,默认定义变量在edata,单时钟存取访问速度快。
-
- edata建议保留1K给堆栈使用,空间不够时可将大数组、不常用变量加xdata关键字定义到xdata空间。
-
- 单色OLED12864显示屏驱动程序,驱动IC为SSD1306,I2C接口,通过I2C将1024字节的图片数据送到屏幕,传送时不占用CPU时间。
-
- 显示图形,汉字,英文,数字.
-
- 其中图形显示发送命令和图片数据使用I2C操作,传输数据时不占用CPU时间。做GUI最方便了,可以先操作定义于xdata的1024字节缓存,然后触发SPI DMA即可,523us或943us即可自己动刷完。
- 本例运行于40MHz, SPI速度为主频4分频(10MHz),每次SPI DMA传输总时间943us,SPI速度为主频2分频(20MHz),每次SPI DMA传输总时间523us。
-
- 将要显示的内容放在1024字节的显存中,启动DMA传输即可。
-
- 下载时, 选择时钟 40MHz (用户可自行修改频率后重新编译即可).
-
- ******************************************/
-
-
- #include "AI8051U.h"
- #include "ASCII6x8.h"
- #include "HZK16.h"
- #include "ASCII-10x24.h"
- #include "picture1.h"
- #include "picture2.h"
-
- #include "stdio.h"
- #include "intrins.h"
-
- //************************************************************************************************
-
- /****************************** 用户定义宏 ***********************************/
-
- #define MAIN_Fosc 40000000UL //定义主时钟
- #define Baudrate 115200L
- #define TM (65536 -(MAIN_Fosc/Baudrate/4))
- #define PrintUart 1 //1:printf 使用 UART1; 2:printf 使用 UART2
- #define Timer0_Reload (65536UL -(MAIN_Fosc / 1000)) //Timer 0 中断频率, 1000次/秒
-
-
- /****************************** IO定义 ***********************************/
- /* 定义接口 */
- //GND AI8051U实验箱 V1.1
- //VCC 3~5V
-
-
- sbit P_OLED_SCL = P3^2; // II2 的时钟脚
- sbit P_OLED_SDA = P3^3; // II2 的数据脚
-
-
-
- /*****************************************************************************/
-
- /************* 本地常量声明 **************/
-
- #define SLAW (0x3C<<1)
- #define SLAR (SLAW +1)
-
- /************* 本地变量声明 **************/
-
- /************* 本地函数声明 **************/
-
- void UartInit(void);
-
-
- void LCD_delay_ms(u16 ms) // 1~65535
- {
- u16 i;
- do
- {
- i = MAIN_Fosc / 6000;
- while(--i) ;
- }while(--ms);
- }
-
- /********************** I2C函数 ************************/
- void Wait()
- {
- while (!(I2CMSST & 0x40));
- I2CMSST &= ~0x40;
- }
-
- void Start()
- {
- I2CMSCR = 0x01; //发送START命令
- Wait();
- }
-
- void SendData(char dat)
- {
- I2CTXD = dat; //写数据到数据缓冲区
- I2CMSCR = 0x02; //发送SEND命令
- Wait();
- }
-
- void RecvACK()
- {
- I2CMSCR = 0x03; //发送读ACK命令
- Wait();
- }
-
- /*
- char RecvData()
- {
- I2CMSCR = 0x04; //发送RECV命令
- Wait();
- return I2CRXD;
- }
-
-
- void SendACK()
- {
- I2CMSST = 0x00; //设置ACK信号
- I2CMSCR = 0x05; //发送ACK命令
- Wait();
- }
-
- void SendNAK()
- {
- I2CMSST = 0x01; //设置NAK信号
- I2CMSCR = 0x05; //发送ACK命令
- Wait();
- }
- */
-
- void Stop()
- {
- I2CMSCR = 0x06; //发送STOP命令
- Wait();
- }
-
- //******************************************
- void OLED_WriteData(u8 dat) //write display data to LCD
- {
- Start(); //发送起始命令
- SendData(SLAW); //发送设备地址+写命令
- RecvACK();
- SendData(0x40); //设置D/C为1,为数据模式
- RecvACK();
- SendData(dat);
- RecvACK();
- Stop(); //发送停止命令
- }
-
- //******************************************
- void OLED_WriteCMD(u8 cmd)
- {
- Start(); //发送起始命令
- SendData(SLAW); //发送设备地址+写命令
- RecvACK();
- SendData(0x00); //设置D/C为0,为指令模式
- RecvACK();
- SendData(cmd);
- RecvACK();
- Stop(); //发送停止命令
- }
-
- //========================================================================
- // 函数: void Set_Dot_Addr_LCD(int x,int y)
- // 描述: 设置在LCD的真实坐标系上的X、Y点对应的RAM地址
- // 参数: x X轴坐标
- // y Y轴坐标
- // 返回: 无
- // 备注: 仅设置当前操作地址,为后面的连续操作作好准备
- // 版本:
- // 2007/04/10 First version
- //========================================================================
- void Set_Dot_Addr(u8 x,u8 y)
- {
- OLED_WriteCMD((u8)(0xb0 + y)); //设置页0~7
- OLED_WriteCMD((x >> 4) | 0x10); //设置列0~127 高nibble
- OLED_WriteCMD(x & 0x0f); //设置列0~127 低nibble
- }
-
-
- //******************************************
- void FillPage(u8 y,u8 color) //Clear Page LCD RAM
- {
- u8 j;
- Set_Dot_Addr(0,y);
- for(j=0; j<128; j++) OLED_WriteData(color);
- }
-
- //******************************************
- void FillAll(u8 color) //Clear CSn LCD RAM
- {
- u8 i;
- for(i=0; i<8; i++) FillPage(i,color);
- }
-
-
-
- //******************************************
- void Initialize_OLED(void) //initialize OLED
- {
-
- LCD_delay_ms(100);
-
-
- OLED_WriteCMD(0xAE); //Set Display Off
-
- OLED_WriteCMD(0xd5); //display divide ratio/osc. freq. mode
- OLED_WriteCMD(0x80); //
-
- OLED_WriteCMD(0xA8); //multiplex ration mode:63
- OLED_WriteCMD(0x3F);
-
- OLED_WriteCMD(0xD3); //Set Display Offset
- OLED_WriteCMD(0x00);
-
- OLED_WriteCMD(0x40); //Set Display Start Line
-
- OLED_WriteCMD(0x8D); //Set Display Offset
- OLED_WriteCMD(0x14);
-
- OLED_WriteCMD(0xA1); //Segment Remap
-
- OLED_WriteCMD(0xC8); //Sst COM Output Scan Direction
-
- OLED_WriteCMD(0xDA); //common pads hardware: alternative
- OLED_WriteCMD(0x12);
-
- OLED_WriteCMD(0x81); //contrast control
- OLED_WriteCMD(0xCF);
-
- OLED_WriteCMD(0xD9); //set pre-charge period
- OLED_WriteCMD(0xF1);
-
- OLED_WriteCMD(0xDB); //VCOM deselect level mode
- OLED_WriteCMD(0x40); //set Vvcomh=0.83*Vcc
-
- OLED_WriteCMD(0xA4); //Set Entire Display On/Off
-
- OLED_WriteCMD(0xA6); //Set Normal Display
-
- OLED_WriteCMD(0xAF); //Set Display On
-
- FillAll(0);
- }
-
-
- //******************************************
-
- void WriteAscii6x8(u8 x,u8 y, u8 number)
- {
- u8 const *p;
- u8 i;
-
- if(x > (128-5)) return;
- if(y >= 8) return;
- p = (u16)number * 6 + ASCII6x8;
-
- Set_Dot_Addr(x,y);
- for(i=0; i<6; i++)
- {
- OLED_WriteData(*p);
- p++;
- }
- }
-
- //=====================================================
- void WriteHZ16(u8 x, u8 y, u16 hz) //向指定位置写一个汉字, x为横向的点0~127, y为纵向的页0~7, hz为要写的汉字.
- {
- u8 const *p;
- u8 i;
-
- if(x > (128-16)) return;
- if(y > 6) return;
- p = hz * 32 + HZK16;
- Set_Dot_Addr(x, y);
- for(i=0; i<16; i++) { OLED_WriteData(*p); p++;}
-
- Set_Dot_Addr(x, (u8)(y+1));
- for(i=0; i<16; i++) { OLED_WriteData(*p); p++;}
- }
-
-
-
- void printf_ASCII_text(u8 x, u8 y, u8 *ptr) //最快写入10个ASCII码(10*6+9=69个字节)耗时430us@24MHZ
- {
- u8 c;
-
- for (;;)
- {
- c = *ptr;
- if(c == 0) return; //遇到停止符0结束
- if(c < 0x80) //ASCII码
- {
- WriteAscii6x8(x,y,c);
- x += 6;
- }
- ptr++;
- }
- }
-
- //******************************************
- void WriteAscii_10x24(u8 x, u8 y, u8 chr) //向指定位置写一个ASCII码字符, x为横向的点0~127, y为纵向的页0~7, chr为要写的字符
- {
- u8 const *p;
- u8 i;
-
- if(x > (128-10)) return;
- if(y >= 6) return;
- p = (u16)chr * 30 + ASCII10x24;
-
- Set_Dot_Addr(x, y);
- for(i=0; i<10; i++) { OLED_WriteData(*p); p++; }
-
- Set_Dot_Addr(x, (u8)(y+1));
- for(i=0; i<10; i++) { OLED_WriteData(*p); p++; }
-
- Set_Dot_Addr(x, (u8)(y+2));
- for(i=0; i<10; i++) { OLED_WriteData(*p); p++; }
- }
-
- //******************************************
- void WriteDot_3x3(u8 x, u8 y) //向指定位置写一个小数点, x为横向的点0~127, y为纵向的页0~7
- {
- if(x > (128-3)) return;
- if(y >= 8) return;
-
- Set_Dot_Addr(x, y);
- OLED_WriteData(0x38);
- OLED_WriteData(0x38);
- OLED_WriteData(0x38);
- }
-
-
- //************ 打印ASCII 10x24英文字符串 *************************
- void printf_ascii_10x24(u8 x, u8 y, u8 const *ptr) //x为横向的点0~127, y为纵向的页0~7, *ptr为要打印的字符串指针, 间隔2点.
- {
- u8 c;
-
- for (;;)
- {
- if(x > (128-10)) return;
- if(y > 5) return;
- c = *ptr;
- if(c == 0) return; //遇到停止符0结束
- if((c >= '0') && (c <= '9')) //ASCII码
- {
- WriteAscii_10x24(x,y,(u8)(c-'0'));
- x += 12;
- }
- else if(c == '.')
- {
- WriteDot_3x3(x,(u8)(y+2));
- x += 6;
- }
- else if(c == ' ') //显示空格
- {
- WriteAscii_10x24(x,y,11);
- x += 12;
- }
- else if(c == '-') //显示空格
- {
- WriteAscii_10x24(x,y,10);
- x += 12;
- }
- ptr++;
- }
- }
-
-
-
-
- //====================================================================================
- u8 xdata DisTmp[1024]; //显示缓冲,将要显示的内容放在显存里,启动DMA即可. 由于LCM DMA有4字节对齐问题,所以这里定位对地址为4的倍数
-
-
- //******************************************
-
-
- void main(void)
- {
- u16 i;
-
- EAXFR = 1; //允许访问扩展寄存器
- WTST = 0;
- CKCON = 0;
-
-
- P0M1 = 0x00; P0M0 = 0x00; //设置为准双向口
- P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口
- P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口
- P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口
- P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口
- P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口
- P6M1 = 0x00; P6M0 = 0x00; //设置为准双向口
- P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口
-
- UartInit();
-
- I2C_S1 =1; //I2C功能脚选择,00:P2.4,P2.3; 01:P1.5,P1.4; 11:P3.2,P3.3
- I2C_S0 =1;
- I2CCFG = 0xc2; //使能I2C主机模式
- I2CPSCR = 0x00; //MSSPEED[13:6]
- I2CMSST = 0x00;
- EA = 1;
- Initialize_OLED();
- printf("SSD1306 OLED 128×64 \r\n"); //串口打印测试
-
- while(1)
- {
- for(i=0; i<1024; i++) DisTmp[i] = 0; //清除显存
-
-
- printf_ASCII_text(0, 0, " OLED12864 SSD1306");
- for(i=0; i<8; i++) WriteHZ16((u8)(i*16),2,i);
- printf_ascii_10x24(0,5,"-12.345 678");
- LCD_delay_ms(3000);
-
- for(i=0; i<1024; i++) DisTmp[i] = gImage_picture1[i]; //将图片装载到显存
-
- LCD_delay_ms(3000);
-
- for(i=0; i<1024; i++) DisTmp[i] = gImage_picture2[i]; //将图片装载到显存
-
- LCD_delay_ms(3000);
- }
- }
-
-
- /******************** 串口打印函数 ********************/
- void UartInit(void)
- {
- #if(PrintUart == 1)
- S1_S1 = 0; //UART1 switch to, 0x00: P3.0 P3.1, 0x40: P3.6 P3.7, 0x80: P1.6 P1.7, 0xC0: P4.3 P4.4
- S1_S0 = 0;
- SCON = (SCON & 0x3f) | 0x40;
- T1x12 = 1; //定时器时钟1T模式
- S1BRT = 0; //串口1选择定时器1为波特率发生器
- TL1 = TM;
- TH1 = TM>>8;
- TR1 = 1; //定时器1开始计时
-
- // SCON = (SCON & 0x3f) | 0x40;
- // T2L = TM;
- // T2H = TM>>8;
- // AUXR |= 0x15; //串口1选择定时器2为波特率发生器
- #else
- S2_S = 1; //UART2 switch to: 0: P1.2 P1.3, 1: P4.2 P4.3
- S2CFG |= 0x01; //使用串口2时,W1位必需设置为1,否则可能会产生不可预期的错误
- S2CON = (S2CON & 0x3f) | 0x40;
- T2L = TM;
- T2H = TM>>8;
- AUXR |= 0x14; //定时器2时钟1T模式,开始计时
- #endif
- }
-
- void UartPutc(unsigned char dat)
- {
- #if(PrintUart == 1)
- SBUF = dat;
- while(TI==0);
- TI = 0;
- #else
- S2BUF = dat;
- while(S2TI == 0);
- S2TI = 0; //Clear Tx flag
- #endif
- }
-
- char putchar(char c)
- {
- UartPutc(c);
- return c;
- }
-
-
复制代码
|