ZY是一名熱愛電子技術的老軟件工程師,觀望了兩期Funpack活動后,在這一期上了車,功能實現得好,代碼和說明還寫得清楚明了,很值得學習。 以下,enjoy。
自我介紹
本人叫ZY,是一名軟件工程師,從事軟件開發工作十多年了,本身的工作是與互聯網相關的應用系統設計開發工作,業余很喜歡電子技術,所以業余有空的時候會玩一些小的電子制作和開發的東西,之前有做過一些函數波形發生器、迷你示波器,探針等小東西。
實現功能
本次也是由于拿到板子太晚,也就僅僅基于作業要求,實現了簡單的電阻測量功能,后續會繼續挖掘更好玩的功能,目前考慮會做一個簡單的示波器,可以通過藍牙或Wi-Fi把數據與Thingsboard結合實現,作業中使用了16bit的ADC,通過分壓測試法實現了電阻測量功能。軟件部分則是基于瑞薩提供的e2studio工具以及FSP2.1.0軟件包,基于HAL庫調用了ADC部分的函數實現的。軟件部分采用了共計12次采樣,拋棄前兩次采樣數據,再去掉最大最小值之后,求平均值的方式實現,同時計算采樣數據的標準差用于評估測試結果的精準程度,這樣可以在僅使用一只10K電阻分壓的條件下,更好的提升測試精度。連接示意圖如下:
測試方法,按上圖將開發版與面包板以及其他器件連接好,將開發板(已下載好程序)的DEBUG USB口與PC端的USB口相連,PC端啟動J-Link RTT Viewer,通過RTT Terminal連接開發版,見下圖
RTT Terminal連接成功后會打印出操作指引,見下圖
下一步將待測電阻插入面包板對應的孔位中,在RTT Terminal中輸入測試指令1,等待約1秒,RTT Terminal中會輸出電阻測試結果(單位歐姆),以及測試結果所對應的采樣平均值與樣本集的標準差。見下圖
代碼說明
hal_entry.c 該文件是程序入口,hal_entry函數中打印了歡迎信息,而后阻塞等待用戶來自RTT Terminal的輸入,用戶輸入后調用adc_ep.c文件中的read_process_input_from_RTT(void) 函數進行命令處理。 adc_ep.c 該文件是主要的程序文件,以下進行簡要的介紹: staticfloat std_dv(uint16_t samples[], uint8_t count)該函數是計算adc樣本向量的標準差,用以評估樣本向量的采樣置信度; staticint32_t f2i(float ft)該函數是將浮點數轉換為整數型,因為RTT Print無法輸出浮點數,因此用此函數將浮點數的整數部分與小數部分分別轉換后輸出; staticvoid print_menus(void)該函數用于輸出菜單項; fsp_err_tread_process_input_from_RTT(void)該函數用于讀取用戶來自RTT Terminal的輸入,調用點在hal_entry.c的hal_entry()函數中; staticvoid resistor_estimation(void)該函數是用于基于adc_ch0的值來估算當前待測電阻的阻值,以下結合代碼說明;
static void resistor_estimation(void){float r = 0;// adc的參考值非常接近于16bit ADC的測量范圍的最大值(32767),因此為了不在除法計算中丟失精度,因此使用乘法計算這類數值。 if (adc_ch0 > 32750) { r = 0.32767 * (32767 - adc_ch0);}// 除法計算對精度影響不大的情況使用除法計算。 else { float adcv = adc_ch0; r = (32767 - adcv) / adc_ch0 * 10000; } int32_t int_v = f2i (r); int32_t deci_v = f2i ((r - int_v) * 1000); APP_PRINT("Estimation of resistor at channel 0 is: %d.%d (Ohm) ", int_v, deci_v);}uint16_tuint16_t_cmp(const void *a, const void *b)該函數用于qsort排序函數; staticfsp_err_t re_sample(void)該函數用于連續讀取12次adc的采樣值,丟棄前2次的采樣值,并對后10次采樣值排序,去掉最大與最小的兩個值,而后對另外的8次采樣值求平均值,以及計算這8次采樣值向量的標準差,以下結合代碼說明;
static fsp_err_t re_sample(void){ fsp_err_t err = FSP_SUCCESS; // Error status uint16_t single_read = 0;//讀取并丟棄第一次ADC采樣的數據 err = R_ADC_Read (&g_adc_ctrl, ADC_CHANNEL_0, &single_read); if (FSP_SUCCESS != err) { APP_ERR_PRINT("** R_ADC_Read API on channel 0 failed ** "); return err; }R_BSP_SoftwareDelay (sample_delay, BSP_DELAY_UNITS_MILLISECONDS);//讀取并丟棄第二次ADC采樣的數據 err = R_ADC_Read (&g_adc_ctrl, ADC_CHANNEL_0, &single_read); if (FSP_SUCCESS != err) { APP_ERR_PRINT("** R_ADC_Read API on channel 0 failed ** "); return err; } R_BSP_SoftwareDelay (sample_delay, BSP_DELAY_UNITS_MILLISECONDS); uint16_t samples[10] = { 0x00 };uint32_t sum_of_adc_reads = 0;//連續讀取10次ADC采樣數據 for (uint8_t i = 0; i < 10; i++) { single_read = 0; err = R_ADC_Read (&g_adc_ctrl, ADC_CHANNEL_0, &single_read); if (FSP_SUCCESS != err) { APP_ERR_PRINT("** R_ADC_Read API on channel 0 failed ** "); return err; } if (1 > single_read || 32767 < single_read) { APP_ERR_PRINT("Read bad data on ADC channel 0, operation abort "); return FSP_ERR_ABORTED; } samples[i] = single_read; single_read = 0; R_BSP_SoftwareDelay (sample_delay, BSP_DELAY_UNITS_MILLISECONDS);}//對10次 ADC采樣的數據排序 qsort (samples, 10, sizeof(uint16_t), uint16_t_cmp); uint16_t valid_samples[8] ={ 0x00 };//去除10次采樣數據中的最大與最小值,并累加求和 for (uint8_t j = 0; j < 8; j++) { valid_samples[j] = samples[j + 1]; sum_of_adc_reads += samples[j + 1];}//計算過濾后的8次采樣數據的標準差std_deviation = std_dv (valid_samples, 8);//計算過濾后的8次采樣數據的平均值并賦值到adc_ch0變量 adc_ch0 = (uint16_t) (sum_of_adc_reads >> 3); return err;}
staticfsp_err_t adc_ch0_read(void)該函數用于啟動并校準16bits ADC,而后調用re_sample函數讀取的通道0的數據,以下結合代碼說明;
static fsp_err_t adc_ch0_read(void){fsp_err_t err = FSP_SUCCESS; // Error status//判斷ADC是否處于被占用的狀態 if (false == adc_busy){ //開啟16 bits ADC模塊 /* Open/Initialize ADC module */ err = R_ADC_Open (&g_adc_ctrl, &g_adc_cfg); if (FSP_SUCCESS != err) { APP_ERR_PRINT("** R_ADC_Open API failed ** "); return err; } // Delay for waiting for ADC be stable R_BSP_SoftwareDelay (5, BSP_DELAY_UNITS_MILLISECONDS); #ifdef BOARD_RA2A1_EK /* Set Reference Voltage Circuit Control register */ R_ADC0->VREFAMPCNT |= ((VREFADCG_VALUE << SHIFT_BY_ONE) | (VREFADCG_ENABLE << SHIFT_BY_THREE)); //根據芯片手冊中的建議,16bits ADC使用前需要進行校準,這里調用函數校準ADC。 /* Calibrate the ADC */ err = adc_start_calibration (); if (FSP_SUCCESS != err) { APP_ERR_PRINT("** adc_start_calibration function failed ** "); return err; }#endif //調用HAL庫中的函數,配置ADC,這里配置為連續讀取模式 /* Configures the ADC scan parameters */ err = R_ADC_ScanCfg (&g_adc_ctrl, &g_adc_channel_cfg); if (FSP_SUCCESS != err) { APP_ERR_PRINT("** R_ADC_ScanCfg API failed ** "); return err; } //啟動ADC采樣 /* Start the ADC scan*/ err = R_ADC_ScanStart (&g_adc_ctrl); if (FSP_SUCCESS != err) { APP_ERR_PRINT("** R_ADC_ScanStart API failed ** "); return err; } adc_busy = true; //調用函數re_sample()函數進行ADC數據采樣,前文有詳細說明該函數。 err = re_sample (); if (FSP_SUCCESS != err) { APP_ERR_PRINT("** Sampling on ADC channel 0 failed ** "); return err; } uint8_t counter = 0; //基于調試結果分析,低阻值的電阻需要依賴采樣的數據的一致性來保證測量精度,因此這里判斷如果為低阻值數據,且采樣數據標準差大于1.5則進行重新采樣處理,共重試最多8次,直至采樣數據符合要求,如果超過8次采樣數據仍舊未能符合標準,則打印測量失敗的通知到RTT Terminal提示用戶。
if (32755 < adc_ch0 && 1.5f < std_deviation) { APP_PRINT( "Lower resistance value [1(Ohm) ~ 5(Ohm)] found and sampling quality too low, auto perform re-sampling... "); while (1.5f < std_deviation && 10 > counter) { err = re_sample (); if (FSP_SUCCESS != err) { APP_ERR_PRINT("** Sampling on ADC channel 0 failed ** "); return err; } counter++; } } if (32755 < adc_ch0 && 1.5f < std_deviation) { APP_ERR_PRINT("Sampling quality too low, operation abort "); } else { //輸出樣本質量評估的標準差數據 int32_t int_stddv = f2i (std_deviation); int32_t deci_stddv = f2i ((std_deviation - int_stddv) * 1000); APP_PRINT("Standard deviation of samples is: %d.%d, samples quality is %s ", int_stddv, deci_stddv, std_deviation < 5 ? "GOOD" : "POOR"); //輸出16 bits ADC采樣的原始數據。 APP_PRINT("Sampling data at ADC channel 0 is: %d ", adc_ch0); //輸出待測電阻的估算阻值。 resistor_estimation (); } //停止ADC連續采樣 err = R_ADC_ScanStop (&g_adc_ctrl); if (FSP_SUCCESS != err) { APP_ERR_PRINT("** R_ADC_ScanStop API failed ** "); return err; } adc_busy = false; //關閉ADC模塊 err = R_ADC_Close (&g_adc_ctrl); if (FSP_SUCCESS != err) { APP_ERR_PRINT("** R_ADC_Close API failed ** "); return err; } } else { APP_PRINT("Resistor testing in progress "); } return err;}
staticfsp_err_t adc_start_calibration(void)該函數用于校準ADC。
活動體會
首先簡單說一下瑞薩的這塊板子,拿到手的比較晚,大概在12月10號左右拿到的,所以了解的也十分有限,因為要趕在20號前交作業,所以還來不及很充分的去看這個板子的內容。這塊評估板不論做工還是設計都是十分不錯的,該有的都有,需要配置和調整的地方也設計的比較靈活,概覽了一下板子的手冊,發現板子可玩性還是比較強的。 其次再說一下e2studio這款開發工具,對于我個人來說,這個開發工具真是太合我心意了。首先我使用eclipse應該快有20年了,從上學到工作中這些年一直也都在使用這個開發工具,十分的熟悉,應該說瑞薩選擇以eclipse作為基礎來打造開發環境,也就是因為eclipse在過去的20年中已經深入民心了,大部分的工程師尤其是軟件工程師應該都有使用eclipse的經驗;另外e2studio針對調試和配置兩個部分做了很好的擴展,雖然界面沒有stm32的那么華麗,但是功能簡潔實用,軟件的穩定程度也很高,在使用過程中還未遇到明顯的錯誤。 最后說一下Funpark活動,這個活動確實很棒,建立交流群讓共同的愛好者有一個共同的目標,一起討論交流,這個是十分愉快也十分難得的;另外還會請來高水平老師給大家進行培訓,幫助大家盡量解決問題,掃清了我們完成任務的障礙;最后是硬禾這個平臺,在前段時間板子沒到的時候,去過硬禾的網站和電子森林的站點,里邊的確是包含了非常多的技術干貨,著實是能幫助到電子愛好者甚至是專業從業人士的。 再次感謝硬禾與Digikey能夠給大家提供這樣一個好玩又能學知識的活動,實在是很棒。
原文標題:看老工程師寫代碼說明——Funpack第三期分享之二
文章出處:【微信公眾號:FPGA入門到精通】歡迎添加關注!文章轉載請注明出處。
責任編輯:haq
-
電子
+關注
關注
32文章
1947瀏覽量
91348 -
代碼
+關注
關注
30文章
4902瀏覽量
70866
原文標題:看老工程師寫代碼說明——Funpack第三期分享之二
文章出處:【微信號:xiaojiaoyafpga,微信公眾號:電子森林】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄


電子工程師自學速成——入門篇

工程師經驗分享:社區之星 趙云 沉著穩定才能做好技術

如何成為一名嵌入式軟件工程師?
工程師手冊:常用電子物料封裝及參數介紹

嵌入式軟件工程師就業好不好?



評論