平衡车-ADC采集电池电压

🌈个人主页:羽晨同学

💫个人格言:"成为自己未来的主人~"

这个是我们平衡车的供电的系统图

这个是对应的电路图,通过R6和R14分压之后,将对应的电压值给到VBAT_SENSE。

上面当中,我们所提到的两节电池电压满电情况下,总和为8.4V,若经过两个电阻的分压,给到单片机的时候,正好为3.3V,符合单片机的电压情况。

现在,我们想要实现的效果为,通过上面的电路图中的3个LED灯显示平衡车电池电压的情况。

接下来,我们说一下我们本次实验的思路:

首先,这个图是我们的时钟树的图,我们要使用的是ADC来采集电压,我们可以看到ADC是在APB2里面的,我们可以通过外部时钟,经过锁相环,然后再经过AHB分频器,然后通过APB2分频器,然后选在ADC中。

这个是我们ADC流程图,我们打算采用的注入序列,而不是采用常规序列,注入序列的优先级是要大于常规序列的,当注入序列和常规序列同时执行的时候,注入序列是可以直接打断常规序列的,然后,我们通过PB0采集电压,每10ms触发外部序列TIM2_TRG0一次,让ADC完成采集,当ADC完成采集之后,产生中断,并将注入序列的结果放入JDR1中(此时JEOC置为1,EOC为常规序列的标志位),然后再中断函数当中读取JDR1的结果进行电压的处理。

所以,我们在工程文件中,创建我们所需要的文件为app_bat.c和app_bat.h

cpp 复制代码
//app_bat.h
#ifndef APP_BAT_H
#define APP_BAT_H

#include "stm32f10x.h"

void App_Bat_Init(void);

#endif //防止头文件重复引用

我们现在.h文件中,创建我们所需要实现的功能

然后再.c文件中具体实现

对于app_bat_init函数,也就是电压检测模块的初始化而言,我们需要做以下操作:

这个是我们对应的时基单元模块,在这个模块当中,从模式控制器有主机和从机两种,从机是通过TRGI输入信号,进而控制嵌入式系统,而主机模式则是通过TRGO输出控制信号,从而控制外面的嵌入式系统的应用。我们想要每10ms进行一次电压的检测,所以,我们要通过TRGO每10ms输出一次控制信号,我们通过TIM2进行控制,TIM2挂载在APB1总线上,APB1最高位36MHZ,然后经过二分频,所以TIM2为72MHZ,通过10ms进行控制,所以时基单元的参数为PSC为71,若OSC为71,则72MHZ/72 = 1MHZ,也就是1000000HZ,所ARR为9999,则1000000/10000 = 100MHZ,也就是10ms。

计数器的方向为上记数,重复计数器为0(只有高级计时器才有)。

cpp 复制代码
//
// @简介:对电池电压监测模块进行初始化
//
void App_Bat_Init(void)
{
	//1. 初始化TIM2_TRGO,每10ms产生一个脉冲
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct = {0};
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period = 9999;
	TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
	TIM_SelectOutputTrigger(TIM2,TIM_TRGOSource_Update);//选择主机模式为Update模式
	TIM_Cmd(TIM2,ENABLE);//闭合时基单元的总开关
	//2. 对ADC进行初始化
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//将ADC的分频系数设置为6分频
	
	//将PB0初始化为模拟模式
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启GPIOB的时钟
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AIN;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	//初始化ADC1的基本参数
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//开启ADC1的时钟
	ADC_InitTypeDef ADC_InitStructc = {0};
	ADC_InitStructc.ADC_ContinuousConvMode = DISABLE;
	ADC_InitStructc.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStructc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStructc.ADC_Mode = DISABLE;
	ADC_InitStructc.ADC_NbrOfChannel = 1;
	ADC_InitStructc.ADC_ScanConvMode = DISABLE;
	ADC_Init(ADC1,&ADC_InitStructc);
	
	//设置注入序列的参数
	ADC_ExternalTrigInjectedConvConfig(ADC1,ADC_ExternalTrigInjecConv_T2_TRGO);//把定时器2的TRGO作为注入序列的外部触发器
	ADC_ExternalTrigInjectedConvCmd(ADC1,ENABLE);
	ADC_InjectedChannelConfig(ADC1,ADC_Channel_8,1,ADC_SampleTime_1Cycles5);
	
	//配置ADC的JEOC中断
	ADC_ITConfig(ADC1,ADC_IT_JEOC,ENABLE);
	// NIVIC 中断优先级分组 0 1 2 3 4
	NVIC_InitTypeDef NVIC_InitStruct = {0};
	NVIC_InitStruct.NVIC_IRQChannel = ADC1_2_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
	NVIC_Init(&NVIC_InitStruct);
	ADC_Cmd(ADC1,ENABLE);
}

我们接下来完成当接收到对应中断的时候,相应的中断处理函数:

cpp 复制代码
//
// @简介:ADC1和ADC2的中断处理函数
//
void ADC1_2_IRQHandler(void)
{
	if(ADC_GetFlagStatus(ADC1,ADC_FLAG_JEOC)==SET)
	{
		ADC_ClearFlag(ADC1,ADC_FLAG_JEOC);
		
		uint16_t jdr1 = ADC_GetInjectedConversionValue(ADC1,ADC_InjectedChannel_1);
		
		vbat = jdr1/4095.0f * 8.4f;
	}

}

最后,我们通过串口2将获取的电压值进行打印出来

cpp 复制代码
static float volatile vbat = 0.0f;

由于其为静态变量,所以我们需要额外的一个函数来获取对应的电压值

cpp 复制代码
//
// @简介:获取电压值 单位是V
//
float App_Bat_Get(void)
{
	return vbat;
}

然后,我们要对串口2进行初始化

cpp 复制代码
#include "app_usart2.h"
//
// @简介:对串口进行初始化
//
void App_Usart_Init(void)
{
	//#1. 对PA2和PA3进行初始化
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	
	//PA2 - Tx - AF_PP
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_2;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//PA3 - RX - IPU
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_3;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//#2. 开启USART2的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
	USART_InitTypeDef Usart_InitStruct = {0};
	Usart_InitStruct.USART_BaudRate = 921600;
	Usart_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	Usart_InitStruct.USART_Parity = USART_Parity_No;
	Usart_InitStruct.USART_StopBits = USART_StopBits_1;
	Usart_InitStruct.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART2,&Usart_InitStruct);
	//#3. 闭合串口2的总开关
	USART_Cmd(USART2,ENABLE);
}

然后,我们创建一个测试函数,将对应的值打印出来

cpp 复制代码
//
// @简介:电池电压监测模块的测试程序,通过串口2把电压发送给电脑
//
void Bat_test(void)
{
	App_Usart_Init();
	App_Bat_Init();
	while(1)
	{
		float volt = App_Bat_Get();
		My_USART_Printf(USART2,"%.3f\n",volt);
		Delay(10);
	
	}
}
相关推荐
路过羊圈的狼1 小时前
STM32的HAL库驱动ADS124S08进行PT100温度采集
stm32·嵌入式硬件·mongodb
李永奉2 小时前
51单片机-实现红外遥控模块教程
单片机·嵌入式硬件·51单片机
辛集电子3 小时前
【STM32】位带操作
stm32·单片机·嵌入式硬件
wei-dong-183797540083 小时前
嵌入式硬件工程师每日提问
嵌入式硬件·电源拓扑结构
MOS管-冠华伟业3 小时前
微硕WSF4012 N+P双沟MOS管,驱动汽车智能座椅“无感”升降气泵
单片机·嵌入式硬件
沐欣工作室_lvyiyi4 小时前
基于单片机的汽车防碰撞刹车系统(论文+源码)
单片机·嵌入式硬件·stm32单片机·汽车·毕业设计
点灯小铭4 小时前
基于51单片机宠物喂食系统设计
单片机·mongodb·毕业设计·51单片机·课程设计·宠物
机器视觉知识推荐、就业指导4 小时前
STM32 外设驱动模块:声音传感器模块
stm32·单片机·嵌入式硬件
亿道电子Emdoor5 小时前
【ARM】MDK-Functions界面设置
stm32·单片机·嵌入式硬件
学不动CV了5 小时前
ARM单片机中断及中断优先级管理详解
c语言·arm开发·stm32·单片机·嵌入式硬件·51单片机