操作OLED,通过三条线(SCK、DO、CS)与OLED相连,这里没有DI是因为2440只会向OLED传数据而不用接收数据。
gpio_spi.c来实现gpio模拟spi,负责spi通讯。对于OLED,有专门的指令和数据格式,要传输的数据内容,在oled.c这一层来实现,负责组织数据。
因此,我们需要实现以上两个文件。
1.SPI初始化
新建一个gpio_spi.c文件,实现SPI初始化SPIInt()
1.1 GPIO init(pinmux管脚等配置)
上图J3为板子pin2pin到OLED的底座。
GPF1作为OLED片选引脚,设置为输出;
GPG4作为OLED的数据(Data)/命令(Command)选择引脚,设置为输出;
GPG5作为SPI的MISO,设置为输入(实际用不到);
GPG6作为SPI的MOSI,设置为输出;
GPG7作为SPI的时钟CLK,设置为输出;
根据gpio相关寄存器进行配置如下:用gpio配置成spi使用的各个引脚。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | void SPIInit( void ){ /* 初始化引脚 */ SPI_GPIO_Init(); } static void SPI_GPIO_Init( void ){ /* GPF1 as OLED_CSn output */ GPFCON &= ~(3<<(1*2)); GPFCON |= (1<<(1*2)); GPFDAT |= (1<<1); //取消OLED_CSn片选,pull up /* GPG2 FLASH_CSn output * GPG4 OLED_DC output * GPG5 SPIMISO input * GPG6 SPIMOSI output * GPG7 SPICLK output */ GPGCON &= ~((3<<(2*2)) | (3<<(4*2)) | (3<<(5*2)) | (3<<(6*2)) | (3<<(7*2))); GPGCON |= ((1<<(2*2)) | (1<<(4*2)) | (1<<(6*2)) | (1<<(7*2))); GPGDAT |= (1<<2); //取消FLASH_CSn 片选,pull up } |
2.OLED初始化
再新建一个oled.c文件,以实现初始化OLEDInit(),这里就对应power up sequence。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | void OLEDInit( void ){ /* 向OLED发命令以初始化 */ OLEDWriteCmd(0xAE); /*display off*/ OLEDWriteCmd(0x00); /*set lower column address*/ OLEDWriteCmd(0x10); /*set higher column address*/ OLEDWriteCmd(0x40); /*set display start line*/ OLEDWriteCmd(0xB0); /*set page address*/ OLEDWriteCmd(0x81); /*contract control*/ OLEDWriteCmd(0x66); /*128*/ OLEDWriteCmd(0xA1); /*set segment remap*/ OLEDWriteCmd(0xA6); /*normal / reverse*/ OLEDWriteCmd(0xA8); /*multiplex ratio*/ OLEDWriteCmd(0x3F); /*duty = 1/64*/ OLEDWriteCmd(0xC8); /*Com scan direction*/ OLEDWriteCmd(0xD3); /*set display offset*/ OLEDWriteCmd(0x00); OLEDWriteCmd(0xD5); /*set osc division*/ OLEDWriteCmd(0x80); OLEDWriteCmd(0xD9); /*set pre-charge period*/ OLEDWriteCmd(0x1f); OLEDWriteCmd(0xDA); /*set COM pins*/ OLEDWriteCmd(0x12); OLEDWriteCmd(0xdb); /*set vcomh*/ OLEDWriteCmd(0x30); OLEDWriteCmd(0x8d); /*set charge pump enable*/ OLEDWriteCmd(0x14); } |
D/C即数据(Data)/命令(Command)选择引脚,它为高电平时,OLED即认为收到的是数据;它为低电平时,OLED即认为收到的是命令。先设置为命令模式,再片选OLED,再传输命令,再恢复成原来的模式和取消片选。
2.1 实现OLED写功能
写命令和写数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 | static void OLEDWriteCmd(unsigned char cmd){ OLED_Set_DC(0); /* command */ OLED_Set_CS(0); /* select OLED */ SPISendByte(cmd); OLED_Set_CS(1); /* de-select OLED */ OLED_Set_DC(1); /* gpio output default is pull up*/ } static void OLEDWriteDat(unsigned char data){ OLED_Set_DC(1); /* data*/ OLED_Set_CS(0); /* select OLED */ SPISendByte(data); OLED_Set_CS(1); /* de-select OLED */ } |
命令模式和片选就是单纯的gpio操作,非常简单如下:
1 2 3 4 5 6 7 8 9 10 11 12 | static void OLED_Set_DC( char val){ if (val) GPGDAT |= (1<<4); else GPGDAT &= ~(1<<4); } static void OLED_Set_CS( char val){ if (val) GPFDAT |= (1<<1); else GPFDAT &= ~(1<<1); } |
2.2 SPISendByte()
还剩下SPISendByte()函数,它属于SPI协议,放在gpio_spi.c里面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | void SPISendByte(unsigned char val){ int i; for (i = 0; i < 8; i++){ SPI_Set_CLK(0); SPI_Set_DO(val & 0x80); //MSB SPI_Set_CLK(1); val <<= 1; } } static void SPI_Set_CLK( char val){ if (val) GPGDAT |= (1<<7); else GPGDAT &= ~(1<<7); } static void SPI_Set_DO( char val){ if (val) GPGDAT |= (1<<6); else GPGDAT &= ~(1<<6); } |
发送数据要满足SPI的时序要求,参考前面的介绍:
SPISendByte是把一个byte数据从高位往低位依次发送到DO。spi配置模式0, 主控先设置CLK为低,由于是MSB, 先传送高位,然后CLK为高,在CLK这个上升沿,DO的数据被锁存,OLED就读取了一位数据。接着左移一位,传输下一位。通过SPI_Set_CLK()和SPI_Set_DO()配置SCK和DO的时序,用gpio模拟出了spi。至此,SPI初始化和OLED初始化就基本完成了,接下来就是OLED显示部分。
这里gpio模拟spi传送时主控没有加延时控制SCK的频率,那是由于jz2440本身cpu运行就很慢,这里不延时也是能满足该款外设的spi传输时序,如果cpu很快,那么需要控制spi时序。
3.驱动显示OLED
如何在OLED上显示一个字符?根据前面一节OLED面板的显示原理。代码实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | static void OLEDSetPos( int page, int col){ OLEDWriteCmd(0xB0 + page); /* page address */ OLEDWriteCmd(col & 0xf); /* Lower Column Start Address */ OLEDWriteCmd(0x10 + (col >> 4)); /* Lower Higher Start Address */ } /* page: 0-7 * col : 0-127 * 字符: 8x16象素 */ void OLEDPutChar( int page, int col, char c){ int i = 0; /* 得到字模 */ const unsigned char *dots = oled_asc2_8x16[c - ' ' ]; /* 发给OLED */ OLEDSetPos(page, col); /* 发出8字节数据 */ for (i = 0; i < 8; i++) OLEDWriteDat(dots[i]); OLEDSetPos(page+1, col); /* 发出8字节数据 */ for (i = 0; i < 8; i++) OLEDWriteDat(dots[i+8]); } /* page: 0-7 * col : 0-127 * 字符: 8x16象素 */ void OLEDPrint( int page, int col, char *str){ int i = 0; while (str[i]){ OLEDPutChar(page, col, str[i]); col += 8; if (col > 127) { col = 0; page += 2; } i++; } } static void OLEDSetPos( int page, int col){ OLEDWriteCmd(0xB0 + page); /* page address */ OLEDWriteCmd(col & 0xf); /* Lower Column Start Address */ OLEDWriteCmd(0x10 + (col >> 4)); /* Lower Higher Start Address */ } static void OLEDClear( void ){ int page, i; for (page = 0; page < 8; page ++){ OLEDSetPos(page, 0); for (i = 0; i < 128; i++) OLEDWriteDat(0); } } |
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | /************************** gpio_spi.c ****************/ #include "s3c24xx.h" /* 用GPIO模拟SPI */ static void SPI_GPIO_Init( void ) { /* GPF1 OLED_CSn output */ GPFCON &= ~(3<<(1*2)); GPFCON |= (1<<(1*2)); GPFDAT |= (1<<1); /* GPG2 FLASH_CSn output * GPG4 OLED_DC output * GPG5 SPIMISO input * GPG6 SPIMOSI output * GPG7 SPICLK output */ GPGCON &= ~((3<<(2*2)) | (3<<(4*2)) | (3<<(5*2)) | (3<<(6*2)) | (3<<(7*2))); GPGCON |= ((1<<(2*2)) | (1<<(4*2)) | (1<<(6*2)) | (1<<(7*2))); GPGDAT |= (1<<2); } static void SPI_Set_CLK( char val) { if (val) GPGDAT |= (1<<7); else GPGDAT &= ~(1<<7); } static void SPI_Set_DO( char val) { if (val) GPGDAT |= (1<<6); else GPGDAT &= ~(1<<6); } void SPISendByte(unsigned char val) { int i; for (i = 0; i < 8; i++) { SPI_Set_CLK(0); SPI_Set_DO(val & 0x80); SPI_Set_CLK(1); val <<= 1; } } void SPIInit( void ) { /* 初始化引脚 */ SPI_GPIO_Init(); } /******************* oled.c****************/ #include "oledfont.h" #include "gpio_spi.h" #include "s3c24xx.h" static void OLED_Set_DC( char val) { if (val) GPGDAT |= (1<<4); else GPGDAT &= ~(1<<4); } static void OLED_Set_CS( char val) { if (val) GPFDAT |= (1<<1); else GPFDAT &= ~(1<<1); } static void OLEDWriteCmd(unsigned char cmd) { OLED_Set_DC(0); /* command */ OLED_Set_CS(0); /* select OLED */ SPISendByte(cmd); OLED_Set_CS(1); /* de-select OLED */ OLED_Set_DC(1); /* */ } static void OLEDWriteDat(unsigned char dat) { OLED_Set_DC(1); /* data */ OLED_Set_CS(0); /* select OLED */ SPISendByte(dat); OLED_Set_CS(1); /* de-select OLED */ OLED_Set_DC(1); /* */ } static void OLEDSetPageAddrMode( void ) { OLEDWriteCmd(0x20); OLEDWriteCmd(0x02); } static void OLEDSetPos( int page, int col) { OLEDWriteCmd(0xB0 + page); /* page address */ OLEDWriteCmd(col & 0xf); /* Lower Column Start Address */ OLEDWriteCmd(0x10 + (col >> 4)); /* Lower Higher Start Address */ } static void OLEDClear( void ) { int page, i; for (page = 0; page < 8; page ++) { OLEDSetPos(page, 0); for (i = 0; i < 128; i++) OLEDWriteDat(0); } } void OLEDInit( void ) { /* 向OLED发命令以初始化 */ OLEDWriteCmd(0xAE); /*display off*/ OLEDWriteCmd(0x00); /*set lower column address*/ OLEDWriteCmd(0x10); /*set higher column address*/ OLEDWriteCmd(0x40); /*set display start line*/ OLEDWriteCmd(0xB0); /*set page address*/ OLEDWriteCmd(0x81); /*contract control*/ OLEDWriteCmd(0x66); /*128*/ OLEDWriteCmd(0xA1); /*set segment remap*/ OLEDWriteCmd(0xA6); /*normal / reverse*/ OLEDWriteCmd(0xA8); /*multiplex ratio*/ OLEDWriteCmd(0x3F); /*duty = 1/64*/ OLEDWriteCmd(0xC8); /*Com scan direction*/ OLEDWriteCmd(0xD3); /*set display offset*/ OLEDWriteCmd(0x00); OLEDWriteCmd(0xD5); /*set osc division*/ OLEDWriteCmd(0x80); OLEDWriteCmd(0xD9); /*set pre-charge period*/ OLEDWriteCmd(0x1f); OLEDWriteCmd(0xDA); /*set COM pins*/ OLEDWriteCmd(0x12); OLEDWriteCmd(0xdb); /*set vcomh*/ OLEDWriteCmd(0x30); OLEDWriteCmd(0x8d); /*set charge pump enable*/ OLEDWriteCmd(0x14); OLEDSetPageAddrMode(); OLEDClear(); OLEDWriteCmd(0xAF); /*display ON*/ } /* page: 0-7 * col : 0-127 * 字符: 8x16象素 */ void OLEDPutChar( int page, int col, char c) { int i = 0; /* 得到字模 */ const unsigned char *dots = oled_asc2_8x16[c - ' ' ]; /* 发给OLED */ OLEDSetPos(page, col); /* 发出8字节数据 */ for (i = 0; i < 8; i++) OLEDWriteDat(dots[i]); OLEDSetPos(page+1, col); /* 发出8字节数据 */ for (i = 0; i < 8; i++) OLEDWriteDat(dots[i+8]); } /* page: 0-7 * col : 0-127 * 字符: 8x16象素 */ void OLEDPrint( int page, int col, char *str) { int i = 0; while (str[i]) { OLEDPutChar(page, col, str[i]); col += 8; if (col > 127) { col = 0; page += 2; } i++; } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
2019-12-16 buildroot教程