【正点原子STM32连载】第二十五章 OLED实验 摘自【正点原子】APM32F407最小系统板使用指南

1)实验平台:正点原子stm32f103战舰开发板V4
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html#

第二十五章 OLED实验

本章将介绍使用APM32F407驱动OLED模块进行显示。通过本章的学习,读者将学习到使用GPIO模拟8080时序以及OLED模块的驱动。
本章分为如下几个小节:
25.1 硬件设计
25.2 程序设计
25.3 下载验证

25.1 硬件设计
25.1.1 例程功能

  1. OLED模块不停地显示ASCII字符和对应的码值
  2. LED0闪烁,指示程序正在运行
    25.1.2 硬件资源
  3. LED
    LED0 - PF9
  4. ATK-MD0096模块
    25.1.3 原理图
    本章实验使用了一个正点原子0.96寸OLED模块,该模块需通过OLED模块延长线与板载的OLED接口进行连接,该接口也可与摄像头模块进行连接,该接口与板载MCU的连接原理图,如下图所示:
    在这里插入图片描述

图25.1.3.1 OLED模块与MCU的连接原理图
25.2 程序设计
25.2.1 Geehy标准库的GPIO驱动
本章实验通过控制与OLED模块连接的GPIO模拟8080时序,实现配置OLED模块并在OLED模块上显示指定字符,对于GPIO,主要涉及GPIO的配置和写操作,需要以下几个步骤:
①:配置GPIO引脚为通用输出模式
②:在与OLED模块通讯时,根据需求控制指定GPIO引脚输出指定电平
在Geehy标准库中对应的驱动函数如下:
①:配置GPIO引脚
请见第10.2.1小节中配置GPIO引脚的相关内容。
②:设置GPIO引脚输出电平
使用Geehy标准库提供的驱动函数设置GPIO引脚输出电平的方式,请见第10.2.1小节中设置GPIO引脚输出电平的相关内容。虽然Geehy标准库已经提供了完整的GPIO驱动函数,但在大多数场景下,调用Geehy标准库中GPIO驱动函数的效率并没有直接操作GPIO寄存器的方式高(至少多了函数的调用和返回),例如,本章实验中将以直接操作寄存器的方式写与OLED模块通讯时模拟的8080时序通讯的数据总线,具体的方式是操作GPIO端口的GPIO端口输出数据寄存器(GPIOx_ODATA),其使用示例,如下所示:

#include "apm32f4xx.h"

void example_fun(void)
{
    /* 设置PA0输出高电平 */
    GPIOA->ODATA |= (1 << 0);
    
    /* 设置PB1输出低电平 */
    GPIOB->ODATA &= ~(1 << 1);
}

25.2.2 OLED驱动
本章介绍使用GPIO模拟8080时序驱动OLED模块,本质就是控制GPIO输出高低电平。OLED驱动主要负责向应用层提供OLED的初始化函数,和各种OLED显示的操作函数。本章实验中,OLED的驱动代码包括oled.c、oled.h和字体文件oledfont.h三个文件。
OLED驱动中,对GPIO相关的宏定义,如下所示:

#define OLED_SPI_RST_PORT	GPIOG
#define OLED_SPI_RST_PIN	GPIO_PIN_15

#define OLED_SPI_CS_PORT	GPIOB
#define OLED_SPI_CS_PIN		GPIO_PIN_7

#define OLED_SPI_RS_PORT	GPIOD
#define OLED_SPI_RS_PIN		GPIO_PIN_6

#define OLED_RST(x) do { x ?									\
    		GPIO_SetBit(OLED_SPI_RST_PORT, OLED_SPI_RST_PIN) :		\
    		GPIO_ResetBit(OLED_SPI_RST_PORT, OLED_SPI_RST_PIN);	\
    } while (0)

#define OLED_CS(x) do { x ?									\
    		GPIO_SetBit(OLED_SPI_CS_PORT, OLED_SPI_CS_PIN) :	\
    		GPIO_ResetBit(OLED_SPI_CS_PORT, OLED_SPI_CS_PIN);	\
    } while (0)

#define OLED_RS(x) do { x ?									\
   		 GPIO_SetBit(OLED_SPI_RS_PORT, OLED_SPI_RS_PIN) :	\
    		GPIO_ResetBit(OLED_SPI_RS_PORT, OLED_SPI_RS_PIN);	\
    } while (0)

#define OLED_WR(x) do { x ?					\
    		GPIO_SetBit(GPIOA, GPIO_PIN_4) :	\
    		GPIO_ResetBit(GPIOA, GPIO_PIN_4);	\
    } while (0)

#define OLED_RD(x) do { x ?					\
    		GPIO_SetBit(GPIOD, GPIO_PIN_7) :	\
    		GPIO_ResetBit(GPIOD, GPIO_PIN_7);	\
    } while (0)
OLED驱动中,OLED的初始化函数,如下所示:
/**
 * @brief	初始化OLED
 * @param	无
 * @retval	无
 */
void oled_init(void)
{
    GPIO_Config_T gpio_init_struct;
    
    RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA);	/* OLED_RW */
    RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOB);	/* OLED_CS、OLED_D5 */
    RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOC);	/* OLED_D0/1/2/3/4 */
    RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOD);	/* OLED_DC、OLED_RD */
    RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOE);	/* OLED_D6/7 */
    RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOG);	/* OLED_RST */
    
    gpio_init_struct.pin	= GPIO_PIN_4;				/* OLED_RW */
    gpio_init_struct.mode	= GPIO_MODE_OUT;			/* 输出模式 */
    gpio_init_struct.speed	= GPIO_SPEED_100MHz;		/* 高速 */
    gpio_init_struct.otype	= GPIO_OTYPE_PP;			/* 推挽输出 */
    gpio_init_struct.pupd	= GPIO_PUPD_UP;				/* 上拉 */
    GPIO_Config(GPIOA, &gpio_init_struct);
    
    gpio_init_struct.pin = GPIO_PIN_7 | GPIO_PIN_6;		/* OLED_CS、OLED_D5 */
    GPIO_Config(GPIOB, &gpio_init_struct);
    
    gpio_init_struct.pin =	GPIO_PIN_6 |				/* OLED_D0/1/2/3/4 */
    							GPIO_PIN_7 |
    							GPIO_PIN_8 |
    							GPIO_PIN_9 |
    							GPIO_PIN_11;
    GPIO_Config(GPIOC, &gpio_init_struct);
    
    gpio_init_struct.pin = GPIO_PIN_6 | GPIO_PIN_7;		/* OLED_DC、OLED_RD */
    GPIO_Config(GPIOD, &gpio_init_struct);
    
    gpio_init_struct.pin = GPIO_PIN_5 | GPIO_PIN_6;		/* OLED_D6/7 */
    GPIO_Config(GPIOE, &gpio_init_struct);
    
    gpio_init_struct.pin = GPIO_PIN_15;					/* OLED_RST */
    GPIO_Config(GPIOG, &gpio_init_struct);
    
    OLED_WR(1);
    OLED_RD(1);
    
    OLED_CS(1);
    OLED_RS(1);
    
    OLED_RST(0);
    delay_ms(100);
    OLED_RST(1);
    
    oled_wr_byte(0xAE, OLED_CMD);
    oled_wr_byte(0xD5, OLED_CMD);
    oled_wr_byte(0x50, OLED_CMD);
    oled_wr_byte(0xA8, OLED_CMD);
    oled_wr_byte(0x3F, OLED_CMD);
    oled_wr_byte(0xD3, OLED_CMD);
    oled_wr_byte(0x00, OLED_CMD);
    oled_wr_byte(0x40, OLED_CMD);
    oled_wr_byte(0x8D, OLED_CMD);
    oled_wr_byte(0x14, OLED_CMD);
    oled_wr_byte(0x20, OLED_CMD);
    oled_wr_byte(0x02, OLED_CMD);
    oled_wr_byte(0xA1, OLED_CMD);
    oled_wr_byte(0xC8, OLED_CMD);
    oled_wr_byte(0xDA, OLED_CMD);
    oled_wr_byte(0x12, OLED_CMD);
    oled_wr_byte(0x81, OLED_CMD);
    oled_wr_byte(0xEF, OLED_CMD);
    oled_wr_byte(0xD9, OLED_CMD);
    oled_wr_byte(0xF1, OLED_CMD);
    oled_wr_byte(0xDB, OLED_CMD);
    oled_wr_byte(0x30, OLED_CMD);
    oled_wr_byte(0xA4, OLED_CMD);
    oled_wr_byte(0xA6, OLED_CMD);
    oled_wr_byte(0xAF, OLED_CMD);
    oled_clear();
}

从上面的代码中可以看出,OLED的初始化函数,就是配置与OLED模块连接的GPIO引脚为输出模式,同时根据通信协议控制GPIO输出高低电平,以完成配置OLED模块的操作。
OLED驱动中,向OLED写一字节数据的函数如下所示:

/**
 * @brief	向OLED写入一个字节
 * @param	data: 要输出的数据
 * @param	cmd : 命令/数据标志
 *   @arg	OLED_CMD : 命令
 *   @arg	OLED_DATA: 数据
 * @retval	无
 */
static void oled_wr_byte(uint8_t data, uint8_t cmd)
{
    oled_data_out(data);
    OLED_RS(cmd);
    OLED_CS(0);
    OLED_WR(0);
    OLED_WR(1);
    OLED_CS(1);
    OLED_RS(1);
}

从上面的代码中可以看出,向OLED写一个字节就是按照8080时序来控制GPIO完成的,其中操作数据总线的函数oled_data_out()是通过直接操作GPIO寄存器完成的,其函数如下所示:

/**
 * @brief	通过拼凑的方法向OLED输出一个8位数据
 * @param	data: 要输出的数据
 * @retval	无
 */
static void oled_data_out(uint8_t data)
{
    GPIOC->ODATA &= ~(0xF<<6);						/* GPIOC[9:6]清零 */
    GPIOC->ODATA |= ((uint16_t)data&0xF)<<6;		/* data[3:0]-->GPIOC[9:6] */
    
    GPIOC->ODATA &= ~(0x1<<11);						/* GPIOC[11]清零 */
    GPIOC->ODATA |= ((uint16_t)(data>>4)&0x1)<<11;	/* data[4]-->GPIOC[11] */
    
    GPIOB->ODATA &= ~(0x1<<6);						/* GPIOB[6]清零 */
    GPIOB->ODATA |= ((uint16_t)(data>>5)&0x1)<<6;	/* data[5]-->GPIOB[6] */
    
    GPIOE->ODATA &= ~(0x3<<5);						/* GPIOE[6:5]清零 */
    GPIOE->ODATA |= ((uint16_t)(data>>6)&0x3)<<5;	/* data[7:6]-->GPIOE[6:5] */
}

通过上面介绍的驱动函数就能够往OLED模块写入命令或数据了,而在OLED模块的显示屏上显示出特定的字符或图案,都是通过OLED模块规定的特定命令来完成的,想深究的读者可以查看正点原子ATK-MD0096模块的用户手册或查看实际使用的OLED模块相关的文档。
25.2.3 实验应用代码
本章实验的应用代码,如下所示:

int main(void)
{
    uint8_t t;
    
    NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_3);	/* 设置中断优先级分组为组3 */
    sys_apm32_clock_init(336, 8, 2, 7);					/* 配置系统时钟 */
    delay_init(168);										/* 初始化延时功能 */
    usart_init(115200);									/* 初始化串口 */
    led_init();											/* 初始化LED */
    oled_init();											/* 初始化OLED */
    oled_show_string(0, 0, "ALIENTEK", 24);
    oled_show_string(0, 24, "0.96' OLED TEST", 16);
    oled_show_string(0, 40, "ATOM 2022/10/15", 12);
    oled_show_string(0, 52, "ASCII:", 12);
    oled_show_string(64, 52, "CODE:", 12);
    oled_refresh_gram();								/* 更新显存到OLED */
    
    t = ' ';
    while (1)
    {
    		oled_show_char(36, 52, t, 12, 1);	/* 显示ASCII字符 */
    		oled_show_num(94, 52, t, 3, 12);	/* 显示ASCII字符的码值 */
    		oled_refresh_gram();				/* 更新显存到OLED */
    		
    		t++;
    		if (t > '~')
    		{
    			t = ' ';
    		}
    		
    		LED0_TOGGLE();
    		delay_ms(500);
    }
}

从上面的代码中可以看出,在初始化完OLED后,便使用函数oled_show_string()将一些字符串写入到本地的显存中,然后使用函数oled_refresh_gram()将本地显存中的数据刷新值OLED上进行显示,最后在while循环中遍历ASCII表中的部分字符,将其轮流显示值OLED上,同时显示字符对应的ASCII码值。
25.3 下载验证
在完成编译和烧录操作后,先将开发板断电,随后将OLED模块通过OLED延长线与开发板进行连接,最后再给开发板供电(在插拔开发板上的接插件和模块时,要求必须断电操作,否则容易烧毁硬件)。程序运行后,可以看到OLED上显示了预期的字符串,同时不断地遍历显示ASCII表中的部分字符,即字符对应的ASCII码值。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值