STM32測量外部電源的電壓 本人在項目中遇到一個需求:使用電池給STM32開發板供電,並需要實時顯示當前電源的電量情況。這個需求可以說是很常見了,但是卻困擾了我整整一個多月。 在收到這個需求的時候我首先想到的就是上網查找相關的技術貼,其中一條名為《基於STM32F103內部AD測量電池電壓》的帖子 ...
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