本帖最后由 akes 于 2024-9-28 20:34 编辑
做好功能但没有人能分享,憋得慌就来写一下文章,也期待能有更好的方法出现。 项目需要1Khz的ADC采集,希望滤波做好,用的ADC+DMA采集控制采样频率在约1Mhz采集到1Kb的数组里,每一毫秒进行一次平均数计算,且不关注这个采集要求其他功能如何分时,只看这个算法耗时,通常来说程序是这样的 unsigned short adc_arr[1024]; unsigned short ave_cal() { unsigned short* p = adc_arr; unsigned short* end = adc_arr + 1024; uint32_t total_num = 0; uint32_t total_num2 = 0; p = adc_arr; while(p != end){ total_num+=*p; p++; } total_num>>=10;//右移1等效于除2,右移10等效于除1024 return total_num; }
这个函数在GD32F103的108MHz工作频率下运行耗时134us 每次循环就加一个数,看起来循环太多次了,一次循环多几个数如何?下面这个是我找到的最合适单次循环相加数,单次计算时间来到了72us unsigned short adc_arr[1024]; unsigned short ave_cal() { unsigned short* p = adc_arr; unsigned short* end = adc_arr + 1024; uint32_t total_num = 0; while(p != end){ total_num+=(*p+*(p+1)+*(p+2)+*(p+3)+*(p+4)+*(p+5)+*(p+6)+*(p+7)); total_num+=(*(p+8)+*(p+9)+*(p+10)+*(p+11)+*(p+12)+*(p+13)+*(p+14)+*(p+15)); p+=16; } total_num>>=10;//右移1等效于除2,右移10等效于除1024 return total_num; } 后来我想到ADC值是12位的,最大4095,用的16bit的数组存储,最大65535,而GD32的cpu是32位的,有没有什么把这些资源全都利用上? 确实有,可以用一个32bit的指针操作数组,每次相加实际上是运行两次16bit的加法,拆分的话,因为这里数据是12bit的,如果每次都是4095那么最多能加到16次,第17次就会溢出,所以第16次做拆分最合适 unsigned short adc_arr[1024]; unsigned short ave_cal() { unsigned int* u32p = (void*)adc_arr; unsigned int* u32end = (void*)(adc_arr + 1024); uint32_t total_num = 0; uint32_t total_num2 = 0; while(u32p != u32end){ //把两个u16当成一个u32相加16次后做分离,计算时间为34us total_num2+=(*(u32p)+*(u32p+1)+*(u32p+2)+*(u32p+3));//单次相加四个数字速度最快 total_num2+=(*(u32p+4)+*(u32p+5)+*(u32p+6)+*(u32p+7)); total_num2+=(*(u32p+8)+*(u32p+9)+*(u32p+10)+*(u32p+11)); total_num2+=(*(u32p+12)+*(u32p+13)+*(u32p+14)+*(u32p+15)); total_num += ((total_num2&0xffff)+(total_num2>>16)); total_num2 = 0; u32p+=16; } total_num>>=10;//右移1等效于除2,右移10等效于除1024 return total_num; } 运行耗时缩小到了34us ,得意 以前我那IMX6ull做 SPI framebuffer驱动的时候就出现了64位循环拷贝数组比32位拷贝数组快得多的事,于是就有了这个想法
|