爱抖腿的虎子哥

导航

基于STM32的外部电源电压检测

STM32测量外部电源的电压

本人在项目中遇到一个需求:使用电池给STM32开发板供电,并需要实时显示当前电源的电量情况。这个需求可以说是很常见了,但是却困扰了我整整一个多月。

在收到这个需求的时候我首先想到的就是上网查找相关的技术贴,其中一条名为《基于STM32F103内部AD测量电池电压》的帖子花费了我大量的时间,最后没能完成测试,偶然的一次尝试我发现了一个新电路

 

图中的电阻可以自行调整,R1和R2电阻的作用是分压,对所测电压进行缩小处理:

   测量所得电压=R2/(R2+R1)×电源电压  

根据该公式可以推出

   电源电压=(R2+R1)/R2×测量所得电压  

R0、R3和R4是对电路起到保护作用的电阻,阻值也可以适当调整。

我在实验中使用了两个相同的电路组合测试两个电压,导致相互之间有影响

 

电路中实测阻值为

R0

213

R5

32.1K

R1

213

R6

19.7K

R2

9.85K

R7

32.1K

R3

8.66K

R8

19.3K

R4

19.7K

R9

8.63K

使用稳压电源测试得到数据并进行拟合得到GPIO口的数值与实际电压的关系:

 

所以得到电压的计算公式:

   电压=数值/4096×2.3583×3.3   

然后根据电路以及该公式,对电池从满电一直到放电结束进行数据采集和分析:

 

采集了近万条数据,拟合出了电压-时间关系以及电量电压关系。

最后运行在开发板上如图:

 

测试代码:

  1 /************************************************
  2  功能:基于STM32单片机正点原子精英板的电池电量检测
  3  输入:特定电路位置的电压值  5  开发者:XAUT—余涛
  6  版本:1.0
  7  最后更新时间:2020/08/16
  8 
  9 ************************************************/
 10 
 11 //初始化GPIO口
 12 void Electricity_Init(void)
 13 {
 14     GPIO_InitTypeDef GPIO_InitStructure;
 15     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PORTA时钟
 16     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PA5 anolog输入     金属传感器
 17     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;        //模拟输入引脚
 18     GPIO_Init(GPIOA, &GPIO_InitStructure);
 19     Adc2_Init();
 20 }
 21 //求幂的函数
 22 double mypow(float x, int y) {
 23     double res = 1;
 24     int i;
 25     for(i = 0; i < y; i++)
 26     {
 27         res *= x;
 28     }
 29     return res;
 30 }
 31 
 32 int main(void)
 33 {
 34     char writeTemp_val[10];  //用于存储resault转为字符后的值
 35     char ElectricityVal[60] = ""; //用于保存电量数据
 36     float voltage = 0; //定义电压变量
 37     int Electricity = 0;   //定义电量数值
 38     int value = 0;
 39     double ElectricityVOL = 0; //电量余量
 40     float temporary = 0; //临时变量
 41     int i = 0;
 42     int time = 0; //可工作时长
 43     FIL fil, newfil;          //文件指针指向打开的文件
 44     FRESULT fileRes;       //文件打开结果
 45     UINT bww = 32;           //写入文件数据类型
 46     u32 total, free;         //内存SD卡的总容量和剩余容量
 47     u8 res = 0;        //扇区格式化结果
 48     vu8 key = 0;       //定义key接收按键信号
 49     usmart_dev.init(SystemCoreClock / 1000000);    //初始化USMART
 50     delay_init();                                                                //延时函数初始化
 51     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                         //设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
 52     uart_init(115200);                                                           //串口初始化为115200,串口监视显示时,波特率应当设为115200
 53     LED_Init();                                                                      //初始化与LED连接的硬件接口
 54     LCD_Init();                                                                      //初始化LCD
 55     BEEP_Init();             //初始化蜂鸣器端口
 56     KEY_Init();           //初始化与按键连接的硬件接口
 57     Electricity_Init();       //初始化GPIO口
 58     W25QXX_Init();                //初始化W25Q128
 59     RTC_Init();                  //RTC初始化
 60     usmart_dev.init(72);        //初始化USMART
 61     my_mem_init(SRAMIN);        //初始化内部内存池
 62     exfuns_init();                            //为fatfs相关变量申请内存
 63     f_mount(fs[0], "0:", 1);                     //挂载SD卡
 64     res = f_mount(fs[1], "1:", 1);                 //挂载FLASH.
 65 
 66     POINT_COLOR = RED;                                                      //设置字体为红色
 67     //显示提示信息
 68     LCD_ShowString(30, 50, 200, 16, 16, "Elite STM32");
 69     LCD_ShowString(30, 70, 200, 16, 16, "Shell Firing Simulation");
 70     LCD_ShowString(30, 110, 200, 16, 16, "ATOM@ALIENTEK");
 71     LCD_ShowString(30, 110, 200, 16, 16, "2021/5/04");
 72     POINT_COLOR = BLUE;                                                     //设置字体为蓝色
 73     LCD_ShowString(30, 130, 200, 16, 16, "Number of launches: 0");            //已发射炮弹实际数量
 74     LCD_ShowString(30, 110, 200, 16, 16, "System is initializing!");            //系统已经准备就绪
 75     LCD_ShowString(30, 150, 200, 16, 16, "Launched: 0");                      //检测到炮弹发射信号次数
 76     LCD_ShowString(30, 170, 200, 16, 16, "Filled in: 0");                     //检测到炮弹装填信号次数
 77     LCD_Clear(WHITE);//清屏
 78     while(font_init())             //检查字库
 79     {
 80         while(SD_Init())//检测不到SD卡
 81         {
 82             LCD_ShowString(30, 110, 200, 16, 16, "SD Card Error!");
 83             delay_ms(500);
 84             LCD_ShowString(30, 110, 200, 16, 16, "Please Check! ");
 85             delay_ms(500);
 86             LED0 = !LED0; //DS0闪烁
 87         }
 88         LCD_ShowString(30, 70, 110, 16, 16, "SD Card OK");
 89         LCD_ShowString(30, 110, 130, 16, 16, "Font Updating...");
 90         key = update_font(20, 110, 16, "0:"); //更新字库
 91         while(key)//更新失败
 92         {
 93             LCD_ShowString(30, 110, 110, 16, 16, "Font Update Failed!");
 94             delay_ms(200);
 95             LCD_Fill(20, 110, 130, 110 + 16, WHITE);
 96             delay_ms(200);
 97         }
 98         LCD_ShowString(30, 110, 110, 16, 16, "Font Update Success!   ");
 99         delay_ms(100);
100         LCD_Clear(WHITE);//清屏
101     }
102     POINT_COLOR = RED;             //设置字体为红色
103     Show_Str(30, 50, 200, 16,  "电量检测测试代码", 16, 0);
104     Show_Str(30, 70, 200, 16,  "XAUT-HuziGe", 16, 0);
105     POINT_COLOR = BLUE;                                                     //设置字体为蓝色
106     Show_Str(30, 110, 200, 16, "系统正在载入!", 16, 0);          //系统已经准备就绪
107     Show_Str(30, 130, 200, 16, "电量检测数值: 0", 16, 0);                   //检测到串口数值
108     Show_Str(30, 150, 200, 16, "电池电压: 0.000V", 16, 0);                   //根据数值转换的电压
109     Show_Str(30, 170, 200, 16, "剩余电量:  0 %", 16, 0);                   //根据电压得到的电量
110     Show_Str(30, 190, 200, 16, "预计还能工作: 0 h", 16, 0);                   //根据电量计算工作时间
111     Disp_Time(30, 90, 16);  //显示时间
112     //delay_ms(100);
113     // RTC_Set(2021,8,16,9,41,0);//设置时间
114 
115     if(res == 0X0D) //FLASH磁盘,FAT文件系统错误,重新格式化FLASH
116     {
117         Show_Str(30, 110, 200, 16, "格式化FLASH...", 16, 0);;    //格式化FLASH
118         res = f_mkfs("1:", 1, 4096); //格式化FLASH,1,盘符;1,不需要引导区,8个扇区为1个簇
119         if(res == 0)
120         {
121             f_setlabel((const TCHAR *)"1:ALIENTEK");    //设置Flash磁盘的名字为:ALIENTEK
122             Show_Str(30, 110, 200, 16, "格式化完成!", 16, 0);    //格式化完成
123         } else Show_Str(30, 110, 200, 16, "格式化失败!", 16, 0);    //格式化失败
124         delay_ms(1000);
125     }
126     LCD_Fill(30, 110, 240, 110 + 16, WHITE);        //清除显示
127     while(exf_getfree("0", &total, &free))    //得到SD卡的总容量和剩余容量
128     {
129         Show_Str(30, 110, 200, 16, "FATFS文件系统加载失败!", 16, 0);
130         delay_ms(200);
131         LED0 = !LED0; //DS0闪烁
132     }
133     POINT_COLOR = BLUE; //设置字体为蓝色
134     Show_Str(30, 110, 200, 16, "文件系统加载完成!       ", 16, 0);
135     delay_ms(1000);
136     fileRes = f_open(&fil, "0:/Electricity.txt", FA_OPEN_ALWAYS | FA_WRITE); //打开文件Electricity.txt,若没有则新建
137     while(FR_OK != fileRes)      //打开文件Electricity.txt,若没有则新建,检查文件打开是否成功
138     {
139         Show_Str(30, 110, 200, 16, "文件打开失败!", 16, 0);
140         delay_ms(500);
141         Show_Str(30, 110, 200, 16, "正在重试!    ", 16, 0);
142         delay_ms(500);
143         LED0 = !LED0; //DS0闪烁
144         fileRes = f_open(&fil, "0:/Electricity.txt", FA_OPEN_ALWAYS | FA_WRITE); //打开文件Electricity.txt,若没有则新建
145         LCD_ShowxNum(30 , 2110, fileRes, 3, 16, 0);    //显示错误类型
146     }
147     f_lseek(&fil, fil.fsize);   //指针指到文件末尾
148     Show_Str(30, 110, 200, 16, "文件一打开测试完成!", 16, 0);
149     while(FR_OK != f_close(&fil)) {}
150     delay_ms(1000);
151     fileRes = f_open(&newfil, "0:/Metal.txt", FA_OPEN_ALWAYS | FA_WRITE); //打开文件Metal.txt,若没有则新建
152     while(FR_OK != fileRes)      //打开文件Metal.txt,若没有则新建,检查文件打开是否成功
153     {
154         Show_Str(30, 110, 200, 16, "文件打开失败!", 16, 0);
155         delay_ms(500);
156         Show_Str(30, 110, 200, 16, "正在重试!    ", 16, 0);
157         delay_ms(500);
158         LED0 = !LED0; //DS0闪烁
159         fileRes = f_open(&newfil, "0:/Metal.txt", FA_OPEN_ALWAYS | FA_WRITE); //打开文件Metal.txt,若没有则新建
160         LCD_ShowxNum(30 , 2110, fileRes, 3, 16, 0);    //显示错误类型
161     }
162     f_lseek(&newfil, newfil.fsize);   //指针指到文件末尾
163     Show_Str(30, 110, 200, 16, "文件二打开测试完成!", 16, 0);
164     delay_ms(1000);
165     while(FR_OK != f_close(&newfil)) {}
166     Show_Str(30, 110, 200, 16, "系统准备就绪!             ", 16, 0);        //系统已经准备就绪
167     while(1)
168     {
169         Disp_Time(30, 90, 16);  //显示时间
170         //连续读取10次取平均值
171         for(i = 0; i < 10; i++)
172         {
173             printf("in for");
174             Electricity = Get_Adc2(ADC_Channel_6);    //读取ADC值通道6  即PA6
175             value += Electricity;
176             delay_ms(10);
177         }
178         Electricity = value / 10;
179         value = 0;
180         strcpy(ElectricityVal, Res_Time());    //将时间写入字符串  Res_Time()函数是作为自己写的,获得当前时间组成的字符串
183         voltage = (float)(Electricity * (2.3583 * 3.3 / 4096) - 1.6494); //计算电压值(新电路测电源)
184         sprintf(writeTemp_val, "\t%4d\t%.3f\n", Electricity, voltage);
185         strcat(ElectricityVal, writeTemp_val);
186         temporary = voltage;
187         ElectricityVOL = ((-285.53) * mypow(voltage, 6) + 5601.4 * mypow(voltage, 5) - 45377 * mypow(voltage, 4) + 194353 * mypow(voltage, 3) - 464275 * mypow(voltage, 2) + 586596 * mypow(voltage, 1) - 306296);
188         if(ElectricityVOL > 1)
189             time = ElectricityVOL / 100 * 32; //满电情况下测试可连续工作32小时
190         else
191             time = 0;
192 
193         LCD_ShowxNum(30 + 10 * 8, 170, ElectricityVOL, 3, 16, 0);
194         LCD_ShowxNum(30 + 14 * 8, 190, time, 2, 16, 0);
195         voltage = temporary;
196         //显示电压到LCD屏幕上
197         LCD_ShowxNum(30 + 14 * 8, 130, Electricity, 4, 16, 0);
198         Electricity = voltage;
199         voltage -= Electricity;
200         voltage *= 1000;
201         voltage += 10000;
202         LCD_ShowxNum(30 + 10 * 8, 150, Electricity, 1, 16, 0);//整数部分
203         LCD_ShowxNum(30 + 12 * 8, 150, voltage, 3, 16, 0);//小数部分
204 
205         //打开文件Electricity.txt,若没有则新建
206         while(FR_OK != f_open(&fil, "0:/Electricity.txt", FA_OPEN_ALWAYS | FA_WRITE)) {};
207         f_lseek(&fil, f_size(&fil));   //指针指到文件末尾
208         f_write(&fil, ElectricityVal, strlen(ElectricityVal), &bww);   //写入读取到的值到Electricity.txt
209         while(FR_OK != f_close(&fil)) {}
210         //每10秒进行一次检测并记录
211         for(i = 0; i < 10; i++)
212         {
213             delay_ms(1000);
214         }
215         //delay_ms(1000 * 60 * 5); //延时时间为10ms,可以检测按键按下的最佳延时
216     }
217 }

 

posted on 2022-07-27 09:46  爱抖腿的虎子哥  阅读(3820)  评论(1编辑  收藏  举报