个人技术分享

一、概述

        因为课程需要的原因,我使用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中,附加测量电路的制作和程序编写全部完成。