s3c2440裸机-spi编程-3-gpio模拟spi驱动OLED

发布者:WanderlustSoul最新更新时间:2024-07-05 来源: elecfans关键字:gpio  模拟spi  驱动OLED 手机看文章 扫描二维码
随时随地手机看文章

操作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使用的各个引脚。

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。

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写功能

写命令和写数据:



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操作,非常简单如下:

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里面:

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面板的显示原理。代码实现如下:

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); } }

完整代码如下:

/************************** 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++; } }


关键字:gpio  模拟spi  驱动OLED 引用地址:s3c2440裸机-spi编程-3-gpio模拟spi驱动OLED

上一篇:s3c2440裸机-I2c编程-1-i2c协议
下一篇:s3c2440裸机-spi编程-2-OLED显示面板

推荐阅读最新更新时间:2026-03-08 04:17

s3c2440裸机-spi编程-3-gpio模拟spi驱动OLED
操作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)选择引脚,
[单片机]
s3c2440裸机-spi编程-1-spi协议
1.spi概述 SPI是串行外设接口(Serial Peripheral Interface)的缩写。是 Motorola 公司推出的一种同步串行接口技术,是一种高速的,全双工,同步的通信总线。 2、SPI优点 支持全双工通信(SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出) 数据传输速率快(I2c一般只能到100-400Khz, SPI高达上百Mhz) 3、缺点 没有指定的流控制,没有应答机制确认是否接收到数据,所以跟IIC总线协议比较在数据可靠性上有一定的缺陷。 4、特点 1):高速、同步、全双工、非差分、总线式 2):主从机通信模式 2.硬体框架 SPI协议,硬件框架如下: SCK:提供时钟 DO:
[单片机]
s3c2440裸机-spi编程-2-OLED显示面板
1.OLED显示面板介绍 以QG-2864TMBEG01这款OLED为例,可见它支持Parallel/i2c/SPI这3种方式对它进行控制,这里仅对它进行SPI控制。它的product Specification见附件。 并行接口时序: SPI串行接口时序 Tr/Tf: 表示spi clk上升/下降延不能超过40ns Tclkl/Tclkh: 表示spi clk低/高电平持续至少20ns Tcycle: 表示spi clk一个时钟周期至少100ns Tdsw/Tdhw: 表示spi data的建立/持续时间至少15ms Tcss:片选建立时间至少20ns Tcsh:片选持续时间至少10ns Tas/Tah:
[单片机]
S3C2440裸机------SPI_FLASH编程
1.spi_flash.h #ifndef _SPI_FLASH_H #define _SPI_FLASH_H void SPIFlashReadID(int *pMID, int *pDID); void SPIFlashInit(void); void SPIFlashEraseSector(unsigned int addr); void SPIFlashProgram(unsigned int addr, unsigned char *buf, int len); void SPIFlashRead(unsigned int addr, unsigned char *buf, int len); #endif
[单片机]
linux上使用J-Link调试S3C2440裸机代码
工具: segger的jlink仿真器 segger的jlink for linux 交叉编译工具链里面的arm-xx-linux-xx-gdb 初始化脚本 工具安装 segger的jlink for linux 进入Segger官网--- Download--- J-Link/J-Trace--- J-Link Software and Documentation Pack。有deb、rpm、tgz三种包可供选择,随便选一个,我选的是tgz。由于我的仿真器是和谐版,所以我不敢选择高版本的J-Link,选择Older versions。下载一个比较老的,我下的是最老的。 解压至你要安装的目录。里面有个README.t
[单片机]
s3c2440调试nandflash裸机程序遇到的问题
按照前面sdram的代码,启动代码里面关看门狗、初始化存储控制器(主要是BANK0的Norflash和BANK6的SDRAM)、设置栈到SDRAM的最高地址,text段的数据直接从Norflash里面取。 代码如下: head.S @************************************************************************* @ File:head.S @ 功能:设置SDRAM,将栈设置到SDRAM,然后继续执行 @************************************************************************* .
[单片机]
S3C2440 裸机程序之音频
/**************************************************************** NAME: u2440mon.c DESC: u2440mon entry point,menu,download HISTORY: Mar.25.2002:purnnamu: S3C2400X profile.c is ported for S3C2410X. Mar.27.2002:purnnamu: DMA is enabled. Apr.01.2002:purnnamu: isDownloadReady flag is added. Apr.10.2002:purnnamu: - Select
[单片机]
s3c2440裸机-时钟编程-2-配置时钟寄存器
1.2440时钟时序 下图是2440时钟配置时序: 1.上电后,nRESET复位信号拉低,此时cpu还无法取指令工作。 2.nRESET复位信号结束后变为高电平,此时cpu开始工作。此时cpu主频FCLK=osc。 3.此时可以配置PLL,经过lock time后,FCLK倍频成新的时钟。 2.如何配置时钟 在参考手册的特性里介绍了S3C2440的工作频率,Fclk最高400MHz,Hclk最高136MHz,Pclk最高68MHz。那么 我们干脆配置FCLK:HCLK:PCLK= 400:100:50 (MHz). 1,先配置lock time 我们取芯片手册上的推荐值。
[单片机]
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved