平衡车-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
//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(只有高级计时器才有)。
//
// @简介:对电池电压监测模块进行初始化
//
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 4NVIC_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);
}
我们接下来完成当接收到对应中断的时候,相应的中断处理函数:
//
// @简介: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将获取的电压值进行打印出来
static float volatile vbat = 0.0f;
由于其为静态变量,所以我们需要额外的一个函数来获取对应的电压值
//
// @简介:获取电压值 单位是V
//
float App_Bat_Get(void)
{return vbat;
}
然后,我们要对串口2进行初始化
#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_PPGPIO_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 - IPUGPIO_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);
}
然后,我们创建一个测试函数,将对应的值打印出来
//
// @简介:电池电压监测模块的测试程序,通过串口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);}
}