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
全部0条评论
快来发表一下你的评论吧 !