ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

为什么基于Zynq+SDIO WiFi的超声方案难以产品化(手持式)?

为什么基于Zynq+SDIO WiFi的超声方案难以产品化(手持式)? 本文基于实际验证数据从低功耗设计与超声系统功耗两个维度剖析Zynq SoC搭配SDIO WiFi在便携超声设备中的产品化瓶颈。一、背景便携超声的功耗挑战便携式超声设备对功耗极为敏感——电池容量有限而超声前端AFE、FPGA信号处理、无线传输三者均为耗电大户。采用Xilinx Zynq SoC如7010集成ARM Cortex-A9与FPGA理论上兼具灵活性与性能但实际产品化时功耗与发热成为首要拦路虎。特别是通过SDIO接口外挂WiFi模块进一步加剧了功耗失控。本文通过两轮实测揭示问题根源。二、Zynq SoC低功耗验证裸机环境2.1 实验平台与工程搭建硬件Zynq 7010核心板 小米快充5.19V供电 USB电流表FPGA工程例化ZYNQ处理器、PLL、QSPI、UART等外设工程截图略FSBL第一阶段引导程序修改xparameters.h添加DDR地址宏定义注释main.c中的DDR自检代码避免低功耗模式下误触发。裸机Helloworld工程修改链接脚本将ps7_ram_0_S_AXI_BASEADDR替换为ps7_ram_1_S_AXI_BASEADDR主程序循环打印计数20次后调用低功耗配置函数sleep_cfg()。在FSBL_BSP修改xparameter.h在此文件中增加/* Definitions for peripheralPS7_DDR_0 */#define XPAR_PS7_DDR_0_S_AXI_BASEADDR 0x00100000#define XPAR_PS7_DDR_0_S_AXI_HIGHADDR 0x3FFFFFFF#ifdef XPAR_PS7_DDR_0_S_AXI_BASEADDR#ifdef XPAR_PS7_DDR_0_S/** DDR Read/write test*/Status DDRInitCheck();if (Status XST_FAILURE) {fsbl_printf(DEBUG_GENERAL,DDR_INIT_FAIL \r\n);/* Error Handling here */OutputStatus(DDR_INIT_FAIL);/** Calling FsblHookFallback instead of Fallback* since, devcfg driver is not yet initialized*/FsblHookFallback();}#endif2.2 低功耗配置步骤sleep_cfg核心操作代码中依次执行注释部分为调试打印实际已屏蔽关闭中断cpsid if使能L2 Cache动态时钟门控写l2cpl310寄存器解锁SLCR写0xDF0D到解锁寄存器使能SCU待机模式设置SCU_CONTROL使能Topswitch时钟停止TOPSW_CLK_CTRL置位设置CP15电源控制寄存器开启动态时钟门控DDR进入自刷新模式代码中已注释实际未启用PLL旁路与关断ARM PLL旁路并断电DDR/IO PLL部分注释降低CPU时钟分频系数ARM_CLK_CTRL分频设为0x3F,执行wfi等待中断指令进入休眠代码如下低功耗代码实现如下相关步骤均在sleep_cfg函数#include stdio.h#include platform.h#include stdio.h#include platform.h#include xil_io.h#define wfi() __asm__(wfi)#define ddrc_ctrl_reg1 0xF8006060#define ddrc_para_reg3 0xF8006020#define ddr_clk_ctrl 0xF8000124#define dci_clk_ctrl 0xF8000128#define aper_reg 0xF800012Cvoid sleep_cfg(void){int data;//xil_printf(Step 1 : ---------------------------------------\n\r);//xil_printf(Disable interrupts. Execute cpsid if.\n\r);//xil_printf(\n\n\n\n);//xil_printf(Step 2 : ---------------------------------------\n\r);//xil_printf(Configure wake-up device.\n\r);//xil_printf(\n\n\n\n);//xil_printf(Step 3 : ---------------------------------------\n\r);//xil_printf(Enable L2 cache dynamic clock gating. Set l2cpl310.reg15_power_ctrl[dynamic_clk_gating_en] 1.\n\r);data Xil_In32(0xF8F02000 0X00000F80);//xil_printf(Before l2cpl310.reg15_power_ctrl[dynamic_clk_gating_en]: %x\n\r,data);data | 0x03;Xil_Out32(0xF8F02000 0X00000F80, data);//xil_printf(Write l2cpl310.reg15_power_ctrl[dynamic_clk_gating_en]: %x\n\r,data);data Xil_In32(0xF8F02000 0X00000F80);//xil_printf(After l2cpl310.reg15_power_ctrl[dynamic_clk_gating_en]: %x\n\r,data);//xil_printf(----------Unlock the SLCR -----------------------\n\r);//xil_printf(SLCR Unlock.\n\r);data Xil_In32(0xF8000000 0X00000008);//xil_printf(Before slcr.Unlock: %x\n\r,data);data 0xDF0D;Xil_Out32(0xF8000000 0X00000008, 0xDF0D);//xil_printf(Write slcr.Unlock: %x\n\r,data);//xil_printf(\n\n\n\n);//data Xil_In32(aper_reg); //clock gate unused peripherals//printf(aper_reg %x\n\r, (unsigned int) data);//data 0x1600001;//Xil_Out32(aper_reg,data);//data Xil_In32(aper_reg);//printf(aper_reg %x\n\r, (unsigned int) data);//xil_printf(Step 4 : ---------------------------------------\n\r);//xil_printf(Enable SCU standby mode. Set mpcore.SCU_CONTROL_REGISTER[SCU_standby_enable] 1.\n\r);data Xil_In32(0xF8F00000 0X00000000);//xil_printf(Before slcr.TOPSW_CLK_CTRL[CLK_DIS]: %x\n\r,data);data | 0x20;Xil_Out32(0xF8F00000 0X00000000, data);//xil_printf(Write slcr.TOPSW_CLK_CTRL[CLK_DIS]: %x\n\r,data);data Xil_In32(0xF8F00000 0X00000000);//xil_printf(After slcr.TOPSW_CLK_CTRL[CLK_DIS]: %x\n\r,data);//xil_printf(\n\n\n\n);//xil_printf(Step 5 : ---------------------------------------\n\r);//xil_printf(Enable topswitch clock stop. Set slcr.TOPSW_CLK_CTRL[CLK_DIS] 1.\n\r);data Xil_In32(0xF8000000 0X0000016C);//xil_printf(Before slcr.TOPSW_CLK_CTRL[CLK_DIS]: %x\n\r,data);data | 0x01;Xil_Out32(0xF8000000 0X0000016C, data);//xil_printf(Write slcr.TOPSW_CLK_CTRL[CLK_DIS]: %x\n\r,data);data Xil_In32(0xF8000000 0X0000016C);//xil_printf(After slcr.TOPSW_CLK_CTRL[CLK_DIS]: %x\n\r,data);//xil_printf(\n\n\n\n);//xil_printf(Step 6 : ---------------------------------------\n\r);//xil_printf(Set cp15.power_control_register[dynamic_clock_gating] 1.\n\r);//xil_printf(Enable write access to some system controlprocessor (CP15) registers.\n\r);//data Xil_In32(0xF8000000 0X00000300);////xil_printf(Before slcr.AP_CTRL: %x\n\r,data);//data | 0x03;//Xil_Out32(0xF8000000 0X00000300, data);////xil_printf(Write slcr.AP_CTRL: %x\n\r,data);//data Xil_In32(0xF8000000 0X00000300);////xil_printf(After slcr.AP_CTRL: %x\n\r,data);//set CP15data mfcp(XREG_CP15_POWER_CTRL);//printf(cp15 Reg %x\n\r, (unsigned int) data);mtcp(XREG_CP15_POWER_CTRL,0x701);data mfcp(XREG_CP15_POWER_CTRL);//printf(cp15 Reg %x\n\r, (unsigned int) data);//xil_printf(\n\n\n\n);//xil_printf(Step 7 : ---------------------------------------\n\r);//xil_printf(Put the external DDR memory into self-refresh mode. Refer to section 10.9.6 DDR Power Reduction.\n\r);//data Xil_In32(ddrc_ctrl_reg1);//data | 0x00001000; //enable standby mode and dynamic clock gating//Xil_Out32(ddrc_ctrl_reg1,data);////data Xil_In32(ddrc_para_reg3);//data | 0x00100000; //enable standby mode and dynamic clock gating//Xil_Out32(ddrc_para_reg3,data);////data Xil_In32(ddr_clk_ctrl);//data 0xFFFFFFF0; //enable standby mode and dynamic clock gating//Xil_Out32(ddr_clk_ctrl,data);////data Xil_In32(dci_clk_ctrl);//data 0xFFFFFFF0; //enable standby mode and dynamic clock gating//Xil_Out32(dci_clk_ctrl,data);//xil_printf(\n\n\n\n);//xil_printf(Step 8 : ---------------------------------------\n\r);//xil_printf(Put the PLLs into bypass mode. Set slcr.{ARM, DDR, IO}_PLL_CTRL[PLL_BYPASS_FORCE] 1.\n\r);data Xil_In32(0xF8000000 0X00000100);//xil_printf(Before slcr.{ARM}_PLL_CTRL[PLL_BYPASS_FORCE]: %x\n\r,data);data | 0x10;Xil_Out32(0xF8000000 0X00000100, data);//xil_printf(Write slcr.{ARM}_PLL_CTRL[PLL_BYPASS_FORCE]: %x\n\r,data);data Xil_In32(0xF8000000 0X00000100);//xil_printf(After slcr.{ARM}_PLL_CTRL[PLL_BYPASS_FORCE]: %x\n\r,data);//xil_printf(\n\n\n\n);//CPU Haltdata Xil_In32(0xF8000000 0X00000104);//xil_printf(Before slcr.{DDR}_PLL_CTRL[PLL_BYPASS_FORCE]: %x\n\r,data);data | 0x10;Xil_Out32(0xF8000000 0X00000104, data);//xil_printf(Write slcr.{DDR}_PLL_CTRL[PLL_BYPASS_FORCE]: %x\n\r,data);data Xil_In32(0xF8000000 0X00000104);//xil_printf(After slcr.{DDR}_PLL_CTRL[PLL_BYPASS_FORCE]: %x\n\r,data);//xil_printf(\n\n\n\n);////data Xil_In32(0xF8000000 0X00000108);////xil_printf(Before slcr.{IO}_PLL_CTRL[PLL_BYPASS_FORCE]: %x\n\r,data);//data | 0x10;//Xil_Out32(0xF8000000 0X00000108, data);////xil_printf(Write slcr.{IO}_PLL_CTRL[PLL_BYPASS_FORCE]: %x\n\r,data);//data Xil_In32(0xF8000000 0X00000108);////xil_printf(After slcr.{IO}_PLL_CTRL[PLL_BYPASS_FORCE]: %x\n\r,data);////xil_printf(\n\n\n\n);//xil_printf(Step 9 : ---------------------------------------\n\r);//xil_printf(Shut down the PLLs. Set slcr.{ARM, DDR, IO}_PLL_CTRL[PLL_PWRDWN] 1.\n\r);data Xil_In32(0xF8000000 0X00000100);//xil_printf(Before slcr.{ARM}_PLL_CTRL[PLL_PWRDWN]: %x\n\r,data);data | 0x02;Xil_Out32(0xF8000000 0X00000100, data);//xil_printf(Write slcr.{ARM}_PLL_CTRL[PLL_PWRDWN]: %x\n\r,data);data Xil_In32(0xF8000000 0X00000100);//xil_printf(After slcr.{ARM}_PLL_CTRL[PLL_PWRDWN]: %x\n\r,data);//data Xil_In32(0xF8000000 0X00000104);////xil_printf(Before slcr.{DDR}_PLL_CTRL[PLL_PWRDWN]: %x\n\r,data);//data | 0x02;//Xil_Out32(0xF8000000 0X00000104, data);////xil_printf(Write slcr.{DDR}_PLL_CTRL[PLL_PWRDWN]: %x\n\r,data);//data Xil_In32(0xF8000000 0X00000104);////xil_printf(After slcr.{DDR}_PLL_CTRL[PLL_PWRDWN]: %x\n\r,data);//while(1)//wfi();//data Xil_In32(0xF8000000 0X00000108);////xil_printf(Before slcr.{IO}_PLL_CTRL[PLL_PWRDWN]: %x\n\r,data);//data | 0x02;//Xil_Out32(0xF8000000 0X00000108, data);////xil_printf(Write slcr.{IO}_PLL_CTRL[PLL_PWRDWN]: %x\n\r,data);//data Xil_In32(0xF8000000 0X00000108);////xil_printf(After slcr.{IO}_PLL_CTRL[PLL_PWRDWN]: %x\n\r,data);//xil_printf(Step 10 : ---------------------------------------\n\r);//xil_printf(Increase the clock divisor to slow down the CPU clock. Set slcr.ARM_CLK_CTRL[DIVISOR] 0x3f.\n\r);data Xil_In32(0xF8000000 0X00000120);//xil_printf(Before slcr.ARM_CLK_CTRL[DIVISOR]: %x\n\r,data);data | (0x3F00);Xil_Out32(0xF8000000 0X00000120, data);//xil_printf(Write slcr.ARM_CLK_CTRL[DIVISOR]: %x\n\r,data);data Xil_In32(0xF8000000 0X00000120);//xil_printf(After slcr.ARM_CLK_CTRL[DIVISOR]: %x\n\r,data);while(1){wfi();}}int main(){init_platform();int i0,j0,k0;while(1){for(i100;i0;i--)for(j100000; j0; j--){}kk1;xil_printf(Hello Lowper Demo,%d\n\r,k);if(k20)break;}sleep_cfg();return 0;2.3 功耗实测对比状态电压电流功率正常运行打印循环5.19V0.14A0.73W进入低功耗模式sleep_cfg后5.19V0.08A0.42W结论裸机下功耗降低约42%但该模式需要CPU停歇、PLL降频无法支持实时超声信号处理仅适用于深度休眠场景。三、完整超声系统功耗验证Linux WiFi3.1 测试环境系统启动后自动运行无线APhostapd、DHCP服务器、以及超声业务进程wifi_board.elf。默认电流1.68A 3.8V约6.4W典型手持超声功耗水平。3.2 逐步剥离耗电模块操作电流变化说明初始状态1.68A全功能运行kill 683hostapd无明显变化关闭AP发射对功耗影响甚微kill 682dhcpd无明显变化DHCP后台开销可忽略echo mem /sys/power/state系统挂起降至约0.75A但系统睡眠无法工作AFE PowerDown关闭模拟前端降至0.64A 3.8V约2.4W数据上传仍正常但前端已不工作3.3 关键发现WiFi模块SDIO接口本身并非最大耗电源关闭hostapd/dhcpd后电流几乎不变。AFE模拟前端是功耗大头关闭后功耗直接下降约60%从6.4W降至2.4W。系统级挂起echo mem虽能降低功耗但无法保持实时采集对产品无实际价值。即便杀掉WiFi进程系统仍维持0.75A睡眠模式对超声系统意义不大因为唤醒恢复时间长且无法同步采集。四、总结为何难以产品化瓶颈维度具体问题实时性与功耗矛盾Zynq运行LinuxWiFi协议栈时CPU负载高无法深度休眠而深度休眠又无法响应超声触发信号。AFE功耗过高即便优化数字部分AFE的功耗仍占主导约4W电池供电下续航不足1小时。SDIO WiFi效率低SDIO接口带宽有限且WiFi发射功耗不低但与AFE相比次要然而其协议栈hostapd/dhcpd增加了系统复杂度和待机电流。散热问题6W以上的持续功耗在小尺寸手持设备中难以被动散热影响用户体验和可靠性。启动与唤醒延迟从挂起恢复需重新初始化DDR、PLL耗时数百毫秒无法满足实时成像需求。五、未来虽然ZynqSDIO WIFI Linux方案手持式产品化比较难但是对于数据采集及其便携式方案没有问题。本文验证数据基于Zynq 7010平台不同厂商模块或软件版本可能有差异请以实际测试为准。
返回列表