当前位置: 首页 > news >正文

嵌入式开发-桥接模式:应用与驱动层解耦

文章目录

    • 概要
    • AD采样功能解耦示例
    • 优势对比

概要

针对大型项目开发,将应用层与驱动层分离,通过函数指针桥接。将硬件相关的寄存器操作封装在驱动层的功能函数,并创建结构体声明一系列函数指针作为“桥”,而上层应用在初始化时为这些结构体内的函数指针(桥)指向(注册)具体的驱动实现函数,后续只允许通过指针接口调用,实现解耦功能。使项目得以多方协作并行开发、且易于移植。

AD采样功能解耦示例

ADC_HAL.C驱动层具体实现

uint8GetTempSensorAdcCheckCmpltFlag(void){uint8 u8Tmp;if(ADIF==1){u8Tmp=1;}// ... 一系列硬件寄存器操作return(u8Tmp);}voidClrTempSensorAdcConvertCmpltFlag(void){ADIF=0U;/* clear INTAD interrupt flag */}uint8GetTempSensorRegAdcsFlag(void){uint8 u8Tmp;if(ADCS==1){u8Tmp=1;}else{u8Tmp=0;}return(u8Tmp);}voidTempSensorAdcSampChannelSelect(uint8 u8Chn){switch(u8Chn){caseU8_AD_CH0:ADS=0;///<选择AIN0break;// ... 一系列硬件寄存器操作default:break;}}voidTempSensorStartAdc(uint8 u8Chn){volatileuint16_tw_count;ADCEN=1U;/* supply AD clock */ADMK=1U;/* disable INTAD interrupt */// ... 一系列硬件寄存器操作}uint16GetTempSensorConvertResult(uint8 u8AdcBitSel){uint16 rsl=0;// ... 一系列硬件寄存器操作return(rsl);}

ADC_Mld.C 桥接抽象层(声明结构体:数据成员、函数指针)

typedefstruct{uint8 u8AdChn;// ... 数据成员 ...uint8 fgSensorErr;uint8 fgSampOk;uint16 u16SampleAD_Max;uint16 u16SampleAD_Min;uint16 u16SampAdcVal;uint8 u8SampCnt;uint16 u16SampAdcSum;uint16 u16AveSampAdcVar;// ... 数据成员 ...uint8(*GetAdcCheckCmpltFlag)(void);// 桥1:检查完成标志void(*ClrAdcConvertCmpltFlag)(void);// 桥2:清除标志uint8(*GetRegAdcsFlag)(void);// 桥3:void(*AdcSampChannelSelect)(uint8 u8Chn);// 桥4:选择采样通道void(*StartAdc)(uint8 u8Chn);// 桥5:启动转换uint16(*GetConvertResult)(uint8 u8AdcBitSel);// 桥6:读取结果}stHalAdcType;stHalAdcType stHalAdcSamp[U8_CFG_HAL_AD_NUM];///<私有变量,只在本模块内使用uint8 u8HalAdcSampTim;//AD采样时基

ADC_App.C 应用层

voidHalAdcInit(void)// 初始化阶段,将驱动层的具体实现函数,注册到应用层的接口结构体中{uint8 i;for(i=0;i<U8_CFG_HAL_AD_NUM;i++){stHalAdcSamp[i].GetAdcCheckCmpltFlag=GetTempSensorAdcCheckCmpltFlag;stHalAdcSamp[i].ClrAdcConvertCmpltFlag=ClrTempSensorAdcConvertCmpltFlag;stHalAdcSamp[i].GetRegAdcsFlag=GetTempSensorRegAdcsFlag;stHalAdcSamp[i].AdcSampChannelSelect=TempSensorAdcSampChannelSelect;stHalAdcSamp[i].StartAdc=TempSensorStartAdc;stHalAdcSamp[i].GetConvertResult=GetTempSensorConvertResult;}}voidHalAdcProc(void)//全程通过 stHalAdcSamp[u8AdChanel] 结构体的函数指针操作硬件:{staticuint8 u8AdChanel=0;staticuint8 u8Staus=0;uint8 u8Tmp;if(0!=GetTimeBase4ms()){// ... 一系列操作}switch(u8Staus){case0:{///<判断是否满足采样触发条件if(0!=GetAdcSampCond(u8AdChanel)){//通过 stHalAdcSamp[u8AdChanel] 结构体的函数指针操作硬件:stHalAdcSamp[u8AdChanel].StartAdc(u8AdChanel);u8Staus=1;}}break;case1:{///<通过 stHalAdcSamp[u8AdChanel] 函数指针,判断是否转换完成if(0!=stHalAdcSamp[u8AdChanel].GetAdcCheckCmpltFlag()){u8Staus=0;stHalAdcSamp[u8AdChanel].ClrAdcConvertCmpltFlag();stHalAdcSamp[u8AdChanel].u16SampAdcVal=stHalAdcSamp[u8AdChanel].GetConvertResult(U8_CFG_HAL_ADC_SAMP_BIT);u8Tmp=HalGetAdcAveValue(u8AdChanel);if(0!=u8Tmp){stHalAdcSamp[u8AdChanel].fgSampOk=1;///<采样完成......}}}break;default:{u8Staus=0;}break;}}

优势对比

传统方法:在应用层直接调用驱动函数痛点:
a. 不同驱动的函数命名风格各异(GetTempSensorConvertResult vs SPI_ADC_Read),换芯片平台,应用层到处改函数名。
b. 参数不统一:有的要通道号,有的要设备地址,把每个硬件的具体访问方式作为形参,暴漏在应用层。
c. 无法批量处理:不能写 for(i=0; i<3; i++) 循环读取,因为每个都是不同的函数。

解耦方法:
a. 易于Mock,可注册假函数注入测试数据进行单元测试。
b. 编写函数指针时,通过规定接口契约(参数类型和个数),调用不同驱动时,即使输入形参都是uint8,(有的是物理通道,有的是设备地址,有的是片选号),而函数指针桥接可以将这些差异封装在驱动内部。

// 应用层完全统一(实际配置在初始化时完成)voidApp_ReadTemps(void){for(uint8 i=0;i<3;i++){// 无论底层是配置的哪种ADC,调用方式完全一致!// 参数U8_CFG_HAL_ADC_SAMP_BIT是配置,不是硬件地址temp[i]=stHalAdcSamp[i].GetConvertResult(U8_ADC_BIT_10);}}

c. 应对产品经理需求变更CH1从ADC换成I2C-ADC,只需更改 HalAdcInit 中注册的回调函数

voidHalAdcInit(void){stHalAdcSamp[1].GetConvertResult=I2C_ADC_ReadWrapper;// 换绑定//.........}

代码中的实际体现:

typedefstruct{uint8 u8AdChn;// ← 这就是"私有数据",存物理通道号// ...uint16(*GetConvertResult)(uint8 u8AdcBitSel);// ↑ 参数只有"采样位数"这个纯逻辑配置,没有硬件地址!}stHalAdcType;

桥接模式:硬件地址封装在stHalAdcSamp[i].u8AdChn中,应用层只传"我要10位精度"这个逻辑需求。
更重要的是让硬件寻址细节下沉到初始化阶段,应用层只处理业务逻辑参数(如采样精度、触发模式),实现真正的解耦。

http://www.jsqmd.com/news/631912/

相关文章:

  • 归并排序力扣题(leetcode)桓
  • 2026年口碑好的商用转轮热交换器公司哪家好 - 行业平台推荐
  • ThinkPHP 8的架构的庖丁解牛
  • Qwen3-ASR-1.7B部署教程:HTTPS反向代理配置保障Web服务安全访问
  • CSDN程序员副业图谱
  • 终极OBS多路推流插件完全指南:如何一键实现多平台同步直播
  • 彻底告别OpenClaw使用焦虑:我给他装上了“透视眼”和“批量克隆模组岳
  • ubuntu搭建k8s 1.35版本
  • c语言基础语法六——结构体(完结)
  • 2026年可靠文件销毁公司技术指南:海关销毁公司/电子产品销毁公司/过期食品销毁公司/饮料销毁公司/上海专业销毁公司/选择指南 - 优质品牌商家
  • 嵌入式MQTT设备注册客户端:轻量级DeviceRegistry深度解析
  • 2026年Q2丙烯酸脂肪族聚氨酯面漆标杆名录:环氧富锌底漆、耐高温漆200℃-500℃、聚氯乙烯防腐漆、醇酸调和漆选择指南 - 优质品牌商家
  • SEN66多参数空气质量传感器嵌入式集成指南
  • AI开发-python-langchain框架(--excle文档加载 )乇
  • AxThread:嵌入式轻量级异步任务调度库
  • 深入理解Harness Engineering:当AI Agent让代码不再稀缺,工程师的价值在哪里?
  • npm 从入门到精通(三):再进阶,掌握版本管理、脚本系统与包发布
  • # 016、AutoSAR CP操作系统(OS)配置与任务调度:那个让我加班到凌晨三点的调度死锁
  • 避坑指南:Orin NX上安装VSCode时如何选择合适的deb包版本
  • 2026年热门的封阳台装门窗精选推荐公司 - 行业平台推荐
  • C++ vs .NET 数组原地反转实测:小数组 C++ 碾压,大数组 .NET 反杀?捶
  • 告别原生JDBC的繁琐:用DBUtils的QueryRunner和BeanHandler重构你的Servlet登录逻辑
  • [特殊字符]FlowAgent执行链路深度解析:RootNode与多节点协作全还原
  • 华硕灵耀14 双屏 UX8406CA 原厂Win11 24H2系统分享下载
  • 后端必备基础:Maven 从入门到实战超详细总结
  • PingCraft:从需求文档到可追踪工作项的 Agent 实践之路段
  • 2026质量好的电动车定位器TOP推荐:GPS定位器/个人定位器/企业车辆定位器/儿童定位器/北斗卫星定位器/单北斗定位器/选择指南 - 优质品牌商家
  • 大模型智能体 (agent)简易流程介绍梢
  • Switch_lib:面向继电器控制的轻量级数字引脚时序管理库
  • HTU21D温湿度传感器驱动开发与I²C通信详解