基于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 }
本文来自博客园,作者:爱抖腿的虎子哥,转载请注明原文链接:https://www.cnblogs.com/XAUT-HuziGe/p/15151773.html