提示:此文章适用 物联网技术应用与维护 技能大赛参考
前言
提示:芯片类型为:STM32L151C8T6(物联网技能大赛开发板):
本章节介绍STM32 MCU的ADC、OLED 屏幕的配置原理及应用开发,ADC&OLED 屏幕的基本应用开发。
提示:以下是本篇文章正文内容,下面案例可供参考
一、实训案例:ADC与OLED综合训练
在STM32L151C8T6开发板上,利用STM32CubeMX和Keil5协同开发,完成以下的功能:
【1】 上电开机后,首选在OLED上显示“新大陆教育”的LOGO图片,然后让LED1与LED2依次点亮,然后熄灭,进行灯光检测。灯光检测结束后,OLED切换至数据显示界面,分3行:
第1行显示:“ www.csdn.net”
第2行显示:“采样值:”
第3行显示:“电压值:”
【2】在主程序中,采用查询的方式,每隔0.3秒对ADC_IN0通道的光敏传感器进行一次电压数据采集,并将采样到的12位数据换算成对应的实际电压值。LED1作为A/D采样指示灯,每采样一次闪烁一下。
【3】每进行完一次光敏传感器的数据采样和电压换算后,将其结果更新到OLED显示屏中相应的位置。如果光敏传感器的电压值小于1.3V,则将LED2灯点亮,反之,将LED2灯关闭。
二、学习目录
一、STM32 ADC(数模转化器)开发基础
1.ADC(数模转化器)概况
- ;ADC:Analog to Digital Converter 指模/数转换器
- 将时间和幅值连续的模拟量转化为时间和幅值离散的数字量,
- 项常用ADC:逐次逼近型、双积分型、∑-∆型。
- 12位逐次逼近型的模拟数字转换器。
- ADC1、ADC2、ADC3共3ADC控制器,可以单独使用,也可以使用双重模式提高采样率。
- A/D转换结果以左对齐或右对齐的方式,存储在16位规则组或者注入组数据寄存器中。
- 按照A/D转换的转化方式来划分,ADC的各个通道的A/D转换可以单次、连续、扫描或间断模式执行;按照A/D转换的组织形式来划分,ADC的模拟输入通道分为规则组和注入组两种。而规则通道和注入通道均有外部触发选项。
- 在实际应用中,可能需要中断规则组的转换,临时对某些通道进行转换,好像这些通道注入了原来的规则组,故称注入组,最多由4个通道组成。
- ADC可以对一组最多16个通道按照指定的顺序逐个进行转换,这组指定的通道称为规则组,最多可以安排16个通道。
//注释:在执行规则通道组扫描转化时,如有例外处理则可启用注入通道组的转换。也就是说,注入通道的转换可以打断规则通道的转换,在注入通道被转换完成之后,规则通道才可以继续转换。
规则通道的转换好比是程序的正常执行,而注入通道组的转换则好比是程序正常执行之外的一个中断处理程序。
2.ADC(数模转化器)数据采样的应用计算
●量程:指ADC所能输入模拟信号类型(信号类型包括单极性和双极性)和电压范围(即电压)
●转换位数:指量化过程中的量化位数,A/D转换输出使用二进制表示转换位数。
●分辨率:指ADC能够分辨模拟信号的最小变换量
●公式:分辨率==量程/2n(转换位数)
●转化时间:A/D完成一次完整的ADC转化,需要的时间要经过采样、保持、量化和编码4个过程。(最大转换速度为1MHZ,在ADCCLK=14M,采样周期为1.5个ADC时钟下得到)
- 课堂小测
有一个温湿度测控系统,已知温湿度传感器在0~100度之间为线性输出,参考电压为5V,采用8位的A/D转换器,0度的时候,测的电压为1.8伏,100度的时候,测的电压为4.3伏。
问:系统的分辨率是多少?采集到数据10010001,表示多大的压力?温度多少?
- 答案
由于采用的是8为ADC,参考电压5V,所以分辨率为:
5 * 1/28 = 5/256 = 0.0195V = 1.95mV
0.0195 * 40 = 0.78 度(最小分辨温度)
由于温度是线性指数变化,设斜率为K,得到温度和电压的关系表达式:
K=(100-0)/(4.3-1.8)=40, y= 40 * (x-1.8)(x为采样电压)
因为10010001B = 91H = 145, 所以0.0195 * 145=2.83V
(2.83V-1.8V) * 40=41.2摄氏度
3.随堂案例:ADC单次数据采样与电压换算
在STM32L151C8T6D开发板上,利用STM32CubeMX和Keil5协同开发,完成以下的功能:
【1】将ADC_IN0设置为12位ADC,右对齐,启用中断。
【2】分别用查询和中断这2种方式,每隔0.5秒采样一次ADC的数据。
【3】将每次读取到的ADC采样值转换为对应电压值,发送到上位机。
【4】LED1作为采样指示灯,在ADC转换过程中点亮,其余时间熄灭。
4.配置外设
双击STM32CubeMX图形化软件
1.选择芯片类型为STM32L151C8,创建工程
2.我们在SYS中配置 串口调试 Debug为 Serial Write
3.我们在RCC中分别配置 高速时钟 High Speed Clock(HSE)、低速时钟 Low Speed Clock(LSE)为 Crystal/Ceramic Resonator。
4.双击点开 时钟配置 Clock Configuartion
5.配置时钟源:如下示例
【1】Input frequency 改为8
【2】PLL Source Mux 选择HSE
【3】PLLMUL 选择8
【4】PLLDIV 选择2
【5】System Clock Mux 选择PLLCLK
注意:ADC参数配置参考
ADC_Setting | ADC的设置 |
---|---|
①Clock Prescaler | ADC 的时钟分频数 |
②Resolution | 分辨率(AD位数) |
③Date Alignment | ADC 数据向左对齐/向右对齐 |
④Scan Coversion Mode | ADC 工作在扫描模式/单次模式(多通道/单通道)可以设置参数为ENABLE / DISABLE(启用/禁用) |
⑤Continuous Conversion Mode ADC | 模数转换工作在连续模式,可以设置这个参数为ENABLE / DISAB LE |
⑥Discontinuous Conversion Mode | ADC 模数转换工作在不连续模式(单次模式)可以设置这个参数为ENABLE / DISABLE |
⑦DMA Continuous Requests | DMA 连续请求 |
⑧End of Conversion Selection | 转换选择结束 |
ADC_Regular_ConversionMode | 转换方式 |
---|---|
①Number of Conversion ADC | 转换的通道数量 |
②External Trigger Conversion Source | ADC 外部触发转换源 |
③External Trigger Conversion Edge | ADC 外部触发转换边沿(上升沿/下降沿触发) |
Rank | 排名 |
---|---|
①Channel | ADC 转换通道 |
②Sampling Time | ADC 转换时间 |
ADC_Iniected_ConversionMode | 注入通道转换模式 |
---|---|
①Number of Conersion | ADC 转换的注入通道数(通道数不为0,以下才可配置) |
②External Trigger Source | ADC 外部触发转换源 |
③External Trigger Edge | ADC 外部触发转换边沿(上升沿/下降沿触发) |
④Injected Conversion Mode | ADC 注入转换通道模式(Number of Conersion不为0,才能配置注入通道其他参数) |
①Channel | ADC转换通道 |
②Sampling Time | ADC 转换时间 |
③Injected Offset | ADC 注入通道的偏值 |
WatchDog | 看门狗 |
---|---|
①Enable Analog WatchDog Mode | 模拟看门狗模式 |
5.在”Pintout view“MCU引脚视图(ADC)
①点击MCU的PA0引脚,弹出选择菜单
②选择菜单中的“ADC_IN0”
③在“GPIO Mode and configuration”参数配置视图选择“Analog”-“ADC”,将“NVIC Settings ”中IN0使能
5.配置UART为异步传输
①在“GPIO Mode and configuration”参数配置视图选择“Configuration”-“UART1”,Mode选择“Asynchronous ”
6.在”Pintout view“MCU引脚视图(LED1)
①点击MCU的PA3引脚,弹出选择菜单
②选择菜单中的“GPIO——Output”
7.必须将工程名称、存储位置、开发环境设置
工程名称不可以出现特殊符号、中文。(否则STM32CubeMX生成MDK会提示项目有问题)
8.配置完成后,双击界面右上角"GENERATE CODE"生成工程代码
5.keil v5 阅读ADC初始化函数源码解析
双击打开project.CSDN.ADC.uvprojx文件
首先进入(最好)编译工程项目,否则有些文件生成才可查找
在生成“adc.c”文件中,STM32CubeMX为上面配置的ADC相应的初始化代码,“MX_ADC_Init()”函数,如下:
void MX_ADC_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc.Init.LowPowerAutoWait = ADC_AUTOWAIT_DISABLE;
hadc.Init.LowPowerAutoPowerOff = ADC_AUTOPOWEROFF_DISABLE;
hadc.Init.ChannelsBank = ADC_CHANNELS_BANK_A;
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.NbrOfConversion = 1;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.DMAContinuousRequests = DISABLE;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_4CYCLES;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
6.ADC(数模转化器)思维导图
7.随堂案例解析(两种方式)
方案一:以阻塞的方式发送
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#include <stdio.h> //引入头文件<stdio.h>
#define LED1_ON() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET) //自定义外设 LED_ON
#define LED1_OFF() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET) //自定义外设 LED_OFF
uint16_t ADC_value=0, ADC_volt=0; //定义保存ADC转化的值,电压的值
uint8_t str_buff[64]; //定义8位缓冲区
void UR1_Send_Info()
{
sprintf((char*)str_buff,"采样值:%d,电压值:%d.%d%dV\r\n",ADC_value,ADC_volt/100,(ADC_volt%100)/10,ADC_volt%10);
HAL_UART_Transmit(&huart1,str_buff,sizeof(str_buff),10000); //阻塞式发送函数
}
void Get_ADC_value() //读取ADC转换结果
{
HAL_ADC_Start(&hadc); //开启ADC
LED1_ON();
if(HAL_OK==HAL_ADC_PollForConversion(&hadc,10)) //等待数据采集完成后10毫秒
{
ADC_value=HAL_ADC_GetValue(&hadc); //ADC_value值=读取的ADC转换结果
ADC_volt=ADC_value *330/4096; //3.3电压值&电压值为小数点后百位 * 2^12(ADC位数)
}
UR1_Send_Info();
LED1_OFF();
HAL_ADC_Stop(&hadc);
}
/* USER CODE END 0 */
//在mian()函数中添加以下代码:
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
Get_ADC_Value(); //启动一个阻塞式的ADC转换并读取数据
UR1_Send_Info(); //向上位机发生采样值和电压值
HAL_Delay(500); //延时0.5秒,再进行下一次ADC采样
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
方案二:以非阻塞的方式发送
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#include <stdio.h> //引入头文件<stdio.h>
#define LED1_ON() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET) //自定义外设 LED_ON
#define LED1_OFF() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET) //自定义外设 LED_OFF
uint16_t ADC_value=0, ADC_volt=0; //定义保存ADC转化的值,电压的值
uint8_t str_buff[64]; //定义8位缓冲区
void UR1_Send_Info()
{
sprintf((char*)str_buff,"采样值:%d,电压值:%d.%d%dV\r\n",ADC_value,ADC_volt/100,(ADC_volt%100)/10,ADC_volt%10);
HAL_UART_Transmit(&huart1,str_buff,sizeof(str_buff),10000); //阻塞式发送函数
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) //ADC中断回调函数 该函数可以在main.c文件下“stm32l1xx_hal_adc_h”下调用
{
if(hadc->Instance ==ADC1) //串口实例指针->实例=ADC1
{
ADC_value =HAL_ADC_GetValue(hadc); //ADC_value值=读取的ADC转换结果
ADC_volt=ADC_value * 330/4096; //3.3电压值&电压值为小数点后百位 * 2^12(ADC位数)
UR1_Send_Info(); //向上位机发生采样值和电压值
LED1_OFF();
}
}
/* USER CODE END 0 */
//在mian()函数中添加以下代码:
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_ADC_Start_IT(&hadc); //启动一个非阻塞式的ADC转换并读取数据
LED1_ON(); //点亮LED1采样指示灯
HAL_Delay(500); //延时0.5秒,再进行下一次ADC采样
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
二、STN32 OLED屏幕开发基础
1.OLED 屏幕概况
- OLED:Organic Light-Emitting Display,有机发光显示。
- LoRa模块所用的OLED 屏幕的分辨率:128*64。
- OLED具备自发光、厚度薄、视角广、功耗低、对比度高、响应速度快、可用于挠曲性面板、使用温度范围广、构造及其制作过程较简单等优异特性,并认为是一种比液晶显示更为先进的新一代平板显示技术。以目前的技术,OLED的尺寸还难以大型化,但是分辨率却可以做得很高。
2.OLED开发相关资源下载
提示:以下OLED 屏幕相关资源将在“6.基于STM32CubeMX的OLED底层驱动函数移植”使用
基于STM32CubeMX的OLED屏驱动程序库(内含4个文件)
【1】XMF_OLED_STM32Cube.c:驱动程序的源文件。
【2】XMF_OLED_STM32Cube.h:驱动程序的头文件。
【3】XMF_OLED_Font.h:字库数据文件。
【4】XMF_OLED_BMP.h:图片数据文件。
取字模软件–PCtoLCD2002
【5】用于生产中文字符和图形图片的数据集合。
3.OLED 屏幕的电路原理图
GPIO管脚 | OLED 管脚 | 芯片引脚 | 信号 |
---|---|---|---|
PB_13 | D0 | CLK | 时钟 |
PB_15 | D1 | MOSI | 数据 |
PB_14 | DC | MISO | 数据/命令选择 |
PB_12 | DS | NSS | 片选 |
4.随堂案例:STM32控制OLED显示
在STM32L151C8T6D开发板上,利用STM32CubeMX和Keil5协同开发,完成以下的功能:
【1】用取字模软件生产一张图片数据,作为开机界面在OLED上显示。
【2】5秒后进入信息界面, 第1行显示网址“www.csdn.net”, 第2行显示中文“橘猫会跳墙”,第3行显示日期“2023-9-12”
5.配置外设
双击STM32CubeMX图形化软件
1.选择芯片类型为STM32L151C8,创建工程
2.我们在SYS中配置 串口调试 Debug为 Serial Write
3.在”Pintout view“MCU引脚视图(OLED)
①点击MCU的PB12、PB13、PB14和PB15引脚,弹出选择菜单
②选择菜单中的“GPIO_Output”
4.必须将工程名称、存储位置、开发环境设置
工程名称不可以出现特殊符号、中文。(否则STM32CubeMX生成MDK会提示项目有问题)
8.配置完成后,双击界面右上角"GENERATE CODE"生成工程代码
6.基于STM32CubeMX的OLED底层驱动函数移植
【1】将4个驱动文件拷贝到工程文件中,和main.c放在同一目录,并将XMF_OLED_STM32Cube.c添加到工程代码文件中,根据所选用的芯片型号,修改XMF_OLED_STM32Cube.h头文件中所用的芯片头文件。
//注释:查看项目中引用的头文件
//注释:文件夹中打开导入的移植文件,找到“xmf_oled_stm32”使用记事本打开
//注释:记事本修改头文件,根据使用的芯片类型选择,例如当前使用STM32L151C8T6D,当前引用无需更改。
【2】在main.c中引入头文件XMF_OLED_STM32Cube.h。
//注释:将“xmf_oled_stm32.c”文件中引用的“#include "xmf_oled_stm32.h”复制到“main.c”文件下
【3】根据硬件电路原理图中,修改XMF_OLED_STM32Cube.h中OLED的引脚定义。
【4】查看OLED_Init(void)初始化函数的源码,根据电路接口和应用需要进行修改。
7.keil v5 阅读OLED初始化函数源码解析
双击打开project.CSDN.OLED.uvprojx文件
首先进入(最好)编译工程项目,否则有些文件生成才可查找
在添加的“xmf_oled_stm32.c”文件中,文件中配置了OLED 屏幕相应的初始化代码,“OLED_Init()()”函数,如下:
//初始化OLED
void OLED_Init()
{
GPIO_InitTypeDef GPIO_InitStruct;
__HAL_RCC_GPIOB_CLK_ENABLE();
//CS
GPIO_InitStruct.Pin = OLED_CS_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(OLED_CS_GPIO, &GPIO_InitStruct);
//DC
GPIO_InitStruct.Pin = OLED_DC_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(OLED_DC_GPIO, &GPIO_InitStruct);
//SCLK
GPIO_InitStruct.Pin = OLED_SCLK_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(OLED_SCLK_GPIO, &GPIO_InitStruct);
//SDIN
GPIO_InitStruct.Pin = OLED_SDIN_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(OLED_SDIN_GPIO, &GPIO_InitStruct);
HAL_GPIO_WritePin(OLED_CS_GPIO, OLED_CS_GPIO_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(OLED_DC_GPIO, OLED_DC_GPIO_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(OLED_SCLK_GPIO, OLED_SCLK_GPIO_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(OLED_SDIN_GPIO, OLED_SDIN_GPIO_PIN, GPIO_PIN_SET);
//RESET THE OLED
#if 0
OLED_RST_Set();
delay_ms(100);
OLED_RST_Clr();
delay_ms(100);
OLED_RST_Set();
#endif
OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
OLED_WR_Byte(0x02,OLED_CMD);//---set low column address
OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
OLED_WR_Byte(0x40,OLED_CMD);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
OLED_WR_Byte(0xCF,OLED_CMD); // Set SEG Output Current Brightness
OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常
OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常
OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
OLED_WR_Byte(0x00,OLED_CMD);//-not offset
OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
OLED_WR_Byte(0x12,OLED_CMD);
OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
OLED_WR_Byte(0x02,OLED_CMD);//
OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7)
OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/
OLED_Clear();
OLED_Set_Pos(0,0);
}
8.随堂案例解析
1.打开字模软件“PCtoLCD2002.exe”,选择模式“图形模式”。
2.双击左上角第二个“文件夹”图标,打开选择要生成字模的图片,点击确定添加。
提示:选择图片必须为 “.bmp” 格式
3.双击软件上方“选项”,自定义格式下拉列表中选择“C51格式”,点阵格式选择阴码行前后缀需要修改,使生成字模与文件“xmf.oled.bmp.h”下图片的字模格式一样。
行前缀: //更改后
行后缀:, //更改后
4.打开在移植阶段添加到工程代码“xmf_oled._stm32.c”项目文件,下拉列表选择“xmf.oled.bmp.h”,将默认图片字模选择删除,添加粘贴复制字模软件生成字模代码。
const unsigned char BMP1[] =
{
//图片字模
}
//用取字模软件生成开机LOGO图片数据,并拷贝到XMF_OLED_BMP.h的数组中。
const unsigned char BMP1[] =
{
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
};
5.重新打开字模软件“PCtoLCD2002.exe”,选择模式“图形模式”。
6.返回字符模式,进入“选项”更改,自定义格式下拉列表中选择“C51格式”,行前后缀需要修改,使生成字模与文件“xmf.oled.font.h”下中文的字模格式一样。
行前缀:{ //更改后
行后缀:}, //更改后
4.打开在移植阶段添加到工程代码“xmf_oled._stm32.c”项目文件,下拉列表选择“xmf.oled.font.h”,将默认中文字模选择删除,添加粘贴复制字模软件生成字模代码。
static const char Hzk[][32]=
{
//汉字字模
}
//用取字模软件生成所需中文字符数据,保持到XMF_OLED_Font.h中的Hzk[]数组。
static const char Hzk[][32]={
{0xEF,0xEF,0x2F,0x00,0x6F,0xFF,0x6D,0x6D,0xAD,0x49,0xE5,0x05,0xE9,0xAD,0xCF,0xFF},
{0xFB,0xFC,0xFF,0x00,0xFF,0xFE,0x01,0xED,0xF5,0x89,0xAC,0x89,0xF5,0x6D,0x01,0xFF},/*"橘",0*/
{0xBF,0xDD,0xEB,0xF7,0x0B,0xFD,0xFB,0x3B,0xA0,0xBB,0x3B,0xBB,0xA0,0x3B,0xFB,0xFF},
{0xF7,0xBB,0x7D,0xBE,0xC0,0xFF,0xFF,0x00,0xBB,0xBB,0x80,0xBB,0xBB,0x00,0xFF,0xFF},/*"猫",1*/
{0xBF,0xBF,0xDF,0xDF,0xAF,0xB7,0xBB,0xBC,0xBB,0xB7,0xAF,0xDF,0xDF,0xBF,0xBF,0xFF},
{0xFF,0xFD,0xBD,0x1D,0xAD,0xB5,0xB9,0xBD,0xBD,0xBD,0xAD,0x9D,0x3D,0xFD,0xFF,0xFF},/*"会",2*/
{0xFF,0xC1,0xDD,0x1D,0xDD,0xC1,0xFF,0xF7,0xEF,0x00,0xFF,0x00,0x5F,0xEF,0xF7,0xFF},
{0xBF,0x81,0xBF,0xC0,0xDD,0xDD,0x7F,0xBD,0xCE,0xF0,0xFF,0x80,0x7F,0x7E,0x0D,0xFF},/*"跳",3*/
{0xDF,0xDF,0x00,0xDF,0xDF,0xBB,0xAB,0x9B,0xBB,0x80,0xBB,0x9B,0xAB,0xBB,0xBF,0xFF},
{0xEF,0xCF,0xE0,0xF7,0xF7,0xFF,0x00,0xBE,0xA2,0xAA,0xA2,0xBE,0x00,0xFF,0xFF,0xFF},/*"墙",4*/
};
7.在工程文件“main.c”添加代码
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern unsigned char BMP1[];
void OLED_display_pic()
{
OLED_Clear();
OLED_DrawBMP(0,0,128,64,BMP1);
}
void oled_display_info()
{
OLED_Clear();
OLED_ShowString(16,0,(uint8_t*)"www.csdn.net");
OLED_ShowCHinese(20,3,0);
OLED_ShowCHinese(38,3,1);
OLED_ShowCHinese(56,3,2);
OLED_ShowCHinese(74,3,3);
OLED_ShowCHinese(92,3,4);
OLED_ShowString(24,6,(uint8_t *)"2023-09-10");
}
/* USER CODE END 0 */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
OLED_Init(); //OLED 初始化
OLED_display_pic(); //显示图片
HAL_Delay(5000); //延迟5秒
oled_display_info(); //显示信息
/* USER CODE END 2 */
三、综合案例步骤
一、配置外设
双击STM32CubeMX图形化软件
1.选择芯片类型为STM32L151,创建工程
2.我们在SYS中配置 串口调试 Debug为 Serial Write
3.我们在RCC中分别配置 高速时钟 High Speed Clock(HSE)、低速时钟 Low Speed Clock(LSE)为 Crystal/Ceramic Resonator。
4.双击点开 时钟配置 Clock Configuartion
5.配置时钟源:
【1】Input frequency 改为8
【2】PLL Source Mux 选择HSE
【3】PLLMUL 选择8
【4】PLLDIV 选择2
【5】System Clock Mux 选择PLLCLK
6.在”Pintout view“MCU引脚视图(LED1、LED2)
①点击MCU的PA3和PB8引脚,弹出选择菜单
②选择菜单中的“GPIO——Output”
7.在”Pintout view“MCU引脚视图(ADC)
①点击MCU的PA0引脚,弹出选择菜单
②选择菜单中的“ADC_IN0”
③在“GPIO Mode and configuration”参数配置视图选择“Analog”-“ADC”,将“NVIC Settings ”中IN0使能
8.在”Pintout view“MCU引脚视图(OLED)
①点击MCU的PB12、PB13、PB14和PB15引脚,弹出选择菜单
②选择菜单中的“GPIO_Output”
提示:不需要再配置 UART (异步传输),ADC随堂案例与该案例有区别
9.必须将工程名称、存储位置、开发环境设置
工程名称不可以出现特殊符号、中文。(否则STM32CubeMX生成MDK会提示项目有问题)
10.配置完成后,双击界面右上角"GENERATE CODE"生成工程代码
二、案例解析
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#define LED1_ON() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET)
#define LED1_OFF() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET)
#define LED2_ON() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET)
#define LED2_OFF() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET)
extern unsigned char BMP1[];
void LED_Check()
{
LED1_ON();
HAL_Delay(500);
LED2_ON();
HAL_Delay(500);
LED1_OFF();
HAL_Delay(500);
LED2_OFF();
HAL_Delay(500);
}
void LED_display_pic() //显示图片
{
OLED_Clear(); //OLED 屏幕清屏
OLED_DrawBMP(0,0,128,64,BMP1); //OLED 屏幕显示图片
}
void LED_display_Info() //显示字体
{
OLED_Clear();
OLED_ShowString(6,0,(uint8_t*)"www.csdn.net");
OLED_ShowCHinese(10,3,19);
OLED_ShowCHinese(28,3,20);
OLED_ShowCHinese(46,3,18);
OLED_ShowString(64,3,(uint8_t*)":");
OLED_ShowCHinese(10,6,16);
OLED_ShowCHinese(28,6,17);
OLED_ShowCHinese(46,6,18);
OLED_ShowString(64,6,(uint8_t*)":");
}
uint16_t ADC_Value=0,ADC_Volt=0; //定义标识采样值、电压值
uint8_t str_buff[64]; //定义数据缓冲区(用于存储数据)
//注意:只有提前声明定义的变量,后面的代码才能引用,否则引用的变量无效。
void LED_display_data() //LED显示数据
{
sprintf((char*)str_buff,"%d",ADC_Value); //重定向:数据类型字符 缓冲区数据“str_buff” 采样值
OLED_ShowString(82,3,str_buff); //显示:str_buff缓冲区数据
sprintf((char*)str_buff,"%d.%d%dV",ADC_Volt/100,(ADC_Volt%100)/10,ADC_Volt%10); //重定向:数据类型字符 缓冲区数据“str_buff” 电压值
//ADC_Volt/100<百位>:电压值除100 (ADC_Volt%100)/10 <十位>:电压值取100余数 除10 ADC_Volt%10 <个位>:除10
OLED_ShowString(82,6,str_buff); //显示:str_buff缓冲区数据
}
void Get_ADC_Value()
{
HAL_ADC_Start(&hadc); //开启ADC数模转换
if(HAL_ADC_PollForConversion(&hadc,10)==HAL_OK) //等待ADC数据采集完成,超时10毫秒
{
ADC_Value=HAL_ADC_GetValue(&hadc); //ADC转换结果(赋值)采样值
ADC_Volt=ADC_Value*330/4096; //电压值=采样值*(电压/位数)
LED_display_data();
}
}
/* USER CODE END 0 */
总结
阻塞方式,启动ADC
HAL_ADC_Start(ADC_HandleTypeDef* hadc);
参数1:开启ADC,开始ADC转换
参数2:hadc:ADC句柄
参数3:HAL 状态
非阻塞方式,启动ADC
HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);
参数1:开启ADC,开始ADC转换
参数2:hadc:ADC句柄
参数3:HAL 状态
等待数据采集完成
HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc,uint32_t Timeout);
参数1:等待ADC转换完成
参数2hadc:ADC句柄
参数3Timeout:超时时间(毫秒)
读取ADC转换结果
HAL_ADC_GetValue(ADC_HandleTypeDef* hadc);
参数1:Get ADC regular group conversion result
参数2:hadc:ADC handle
参数3:ADC group regular conversion data
阻塞方式,停止ADC
HAL_ADC_Stop(ADC_HandleTypeDef* hadc);
参数1:Stop ADC conversion of regular group(and injected channels in case of auto_injected mode),disable ADC peripheral
参数2:hadc:ADC handle
参数3:ADC status
非阻塞方式,停止ADC
HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);
参数1:Stop ADC conversion of regular group(and injected channels in case of auto_injected mode),disable ADC peripheral
参数2:hadc:ADC handle
参数3:ADC status
阻塞式串口发送方式函数
HAL_UART_Transmit(UART_HandleTypeDef &huart1 uint8_t *pData,uint16_t size,uint32_t Timeout);
参数1:huart,串口实例指针
参数2:pData,待发送数据缓冲区的指针
参数3:size,待发送数据的字节数
参数4:Timeout,超时时间值
ADC串口中断回调函数
HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
参数1:huart,串口实例指针
ShowString(阿拉伯数字、英文以及各种标点符号 宽度占位8像素 高度占位2像素)
ShowCHinese(汉字 占位16像素 高度占位3像素)
OLED初始函数
void OLED_Init(void); //OLED初始化函数
void OLED_Clear(void); //OLED清屏函数
OLED显示英文字符串函数
OLED_ShowString(unsigned char x,unsigned char y,unsigned char *p);
参数1:x,起点列坐标,0~127
参数2:y,起点行坐标,0~7
参数3:*p,字符串指针
OLED显示中文字符串函数
OLED_ShowCHinese(unsigned char x,unsigned char y,unsigned char no);
参数1:x,起点列坐标,0~127
参数2:y,起点行坐标,0~7
参数3:no,待显示中文字符在数组 Hzk[][32] 中的位置。
OLED显示英文字符串函数
OLED_ShowString(unsigned char x,unsigned char y,unsigned char *p);
参数1:x,起点列坐标,0~127
参数2:y,起点行坐标,0~7
参数3:*p,字符串指针
OLED显示图片函数
OLED_DrawBMP( unsigned char x0, unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[] );
参数1:x0,起点列坐标,0~127
参数2:y0,起点行坐标,0~7
参数3:x1,图片的列范围,1~128
参数4:y1,图片的行范围,1~8
参数5:BMP[],待显示图片数据的数组。
OLED输出变量图片
extern unsigned char BMP1[];
参数1:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中
参数2:BMP1[],数组