一、概述
因为课程需要的原因,我使用stm32f103c8t6单片机编写电压测量,用来对传感器的输出电压进行读取。并将单片机读取出来的电压值在OLED屏幕上显示。
二、引脚设置
(一)概述
我们使用面包板进行操作,实际所需要stm32f103c8t6端口有三个,我选择PA0作为端口电压读取端口,PB8和PB9作为与OLED显示屏链接的端口。由于与OLED显示屏的链接不是我们所叙述的重点,不过多的叙述。重点说明与电压测量有关的PA0端口配置。
(二)具体设置
PA0设置为模拟输入,PB8和PB9全部设置为开漏输出。
三、代码实现
(一)概述
使用Keil uVision5进行代码编写。
(二)具体实现
1.主要函数
首先为主函数针对各个引脚的初始化部分,代码如下所示。这里将所有引脚的初始化全部封装为相应的函数。首先是GPIO引脚的初始化;其次是将首先初始化后的GPIO引脚配置为ADC模式,用于读取电压;最后是OLED屏幕所需要引脚的初始化,将读取电压进行可视化显示。
GPIO_Config();
ADC_Config();
OLED_Init();
2.GPIO_Config()函数
这里我们初始化两个引脚,其中PA0设置为模拟输入模式,用于后续的ADC配置,来读取电压;PA1设置为推挽输出,用于点亮LED灯。
void GPIO_Config(void) {
// 需要注意系列号,不同系列号使用的库函数以及写法不同
// 初始化时钟信号
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置引脚
// 引脚A0用于读取电压
GPIO_InitTypeDef GPIO_InitStructure; // 定义结构体变量
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; // 设置为模拟输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 选择A0号引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 没有特别追求频率的话,50就可以
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 引脚A1用于在传感器受到遮挡时,使LED发光
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; // 选择A1号引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
3.ADC_Config()函数
void ADC_Config(void) {
// 启用ADC1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// ADC 初始化结构体
ADC_InitTypeDef ADC_InitStruct;
// 配置 ADC 参数
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
ADC_InitStruct.ADC_ScanConvMode = DISABLE;
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStruct.ADC_NbrOfChannel = 1;
// 初始化 ADC
ADC_Init(ADC1, &ADC_InitStruct);
// 配置 ADC 通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
// 启用 ADC
ADC_Cmd(ADC1, ENABLE);
// 校准 ADC
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));
// 启动 ADC 转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
4.OLED_Init()函数
是使用购买OLED显示屏时,商家所提供的对应库函数文件,不需要自己进行重新编写,只需调用商家所提供好的接口进行输入。下面主要展示对引脚的初始化部分,如下所示,使用PB8和PB9两个引脚。
/*引脚配置*/
#define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(x))
#define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)(x))
/*引脚初始化*/
void OLED_I2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOB, &GPIO_InitStructure);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
5.主函数
主函数中功能性循环函数如下所示,用于处理端口接受到的电压值,将其处理之后,在OLED屏幕上显示。显示逻辑比较简单。
while (1)
{
// 读取 ADC 值
uint16_t adc_value = ADC_GetConversionValue(ADC1);
// 将 ADC 值转换为电压值(假设参考电压为3.3V,12位 ADC)
float voltage = (adc_value * 3.3) / 4095;
// 整数部分
uint32_t integer = (uint32_t)voltage;
// 小数点第一位
uint32_t pointOne = (uint32_t)(( voltage - (uint32_t)voltage ) * 10);
// 小数点第二位
uint32_t pointTwo = (uint32_t)((( voltage - (uint32_t)voltage ) * 10 - pointOne) * 10;
// 在屏幕上显示
OLED_ShowString(1, 1, "voltage:");
OLED_ShowNum(2, 1, integer, 1);
OLED_ShowChar(2, 2, '.');
OLED_ShowNum(2, 3, pointOne, 1);
OLED_ShowNum(2, 4, pointTwo, 1);
OLED_ShowChar(2, 5, 'V');
}
(三)烧录
将上述程序使用keil uvision5进行编译,发现无报错后烧录入stm32f103c8t6中,附加测量电路的制作和程序编写全部完成。