单总线的抽象分层!

一起学嵌入式 2024-03-06 08:18

扫描关注一起学嵌入式,一起学习,一起成长

1.前言

onewire(单总线) 是DALLAS公司推出的外围串行扩展总线技术总线,顾名思义,它是采用一根信号线进行通信,既传输时钟信号又传输数据,而且能够进行双向通信,具有节省I/O口线、资源结构简单、成本低廉、便于总线扩展和维护等诸多优点。

常用到单总线的器件,一般是温度传感器、EEPROM、唯一序列号芯片等,如DS18B20、DS2431。

在使用单总线时,往往很少CPU会提供硬件单总线,几乎都是根据单总线标准的时序图,通过普通IO翻转模拟实现单总线。

而在模式实现时序图的过程中,需要根据CPU时钟频率等条件进行时序时间计算,如果更换CPU后,需要重新计算时序时间,如果时序代码和器件外设控制代码集成在一起,则代码改动比较大。

或者同一CPU需要模拟多根单总线时,传统的“复制”方式使得程序显得累赘,还增加ROM占用空间。因此,可以利用“函数指针”的方式,将时序部分抽象出来,达到“复用”代码的效果,减少重复代码编写。

2.onewire 抽象

2.1 onewire 结构体

onewire结构体主要是对与CPU底层相关的操作抽象分离,调用时只需将该结构体地址(指针)作为函数入口参数,通过该指针实现对底层函数的回调。该结构体我们命名为“struct ops_onewire_dev”,其原型如下:

struct ops_onewire_dev
{

 void (*set_sdo)(int8_t state);
    uint8_t (*get_sdo)(void);
    void (*delayus)(uint32_t us);
};

其中:1)set_sdo:IO输出1bit,包括时钟和数据。2)get_sdo:IO输入1bit,包括时钟和数据。3)delayus:时序延时函数,根据CPU频率进行计算。

2.2 onewire 对外接口

extern uint8_t ops_onewire_reset(struct ops_onewire_dev *onewire);
extern int ops_onewire_read(struct ops_onewire_dev *onewire,void *buff,int size);
extern int ops_onewire_write(struct ops_onewire_dev *onewire,void *buff,int size);

1)分别为复位函数、读函数、写函数。2)入口首参数为“struct ops_onewire_dev”结构体指针,此部分就是硬件层相关,需要后期初始化的. 3)其余入口参数易于理解,读/写缓存及数据大小。

2.3 onewire 抽象接口实现

分别实现上述三者函数接口。

2.3.1 复位函数

复位函数,在单总线初始化外设器件时需要用到,用于判断总线与器件是否通信上,类似“握手”的动作。

如图,为DS18B20的复位时序图,以下与单总线相关的时序图,都是以DS18B20为例,因为此芯片为单总线应用的经典。

根据时序图,实现复位函数。

/**
  * @brief  单总线复位时序
  * @param  onewire 总线结构体指针
  * @retval 成功返回0
*/

uint8_t ops_onewire_reset(struct ops_onewire_dev *onewire)
{
 uint8_t ret = 0;
 
 onewire->set_sdo(1);
 onewire->delayus(50);
 onewire->set_sdo(0);
 onewire->delayus(500);
 onewire->set_sdo(1);
 onewire->delayus(40);
 ret = onewire->get_sdo();
 onewire->delayus(500);
 onewire->set_sdo(1);
 return ret;
}

2.3.2 读函数

读函数即以该函数,通过单总线从外设上读取数据,至于代码的实现,完全是时序图的实现,无特殊难点。

先实现单字节读函数,再通过调用单字节读函数实现多字节读函数。

/**
  * @brief  单总线读取一字节数据
  * @param  onewire 总线结构体指针
  * @retval 返回读取的数据
*/

static char ops_onewire_read_byte(struct ops_onewire_dev *onewire)
{
 char data = 0;
 uint8_t i;
 
 for(i=8;i>0;i--)
 {
  data >>= 1;
  onewire->set_sdo(0);
  onewire->delayus(5);
  onewire->set_sdo(1);
  onewire->delayus(5);
  if(onewire->get_sdo())
   data |= 0x80;
  else
   data &= 0x7f;
  onewire->delayus(65);
  onewire->set_sdo(1);
 }
 return data;
}

/**
  * @brief  读取多字节
  * @param  onewire 总线结构体指针
  * @param  buff 存放数据缓存
  * @param  size 数据大小
  * @retval 返回读取到的数据大小
*/

int ops_onewire_read(struct ops_onewire_dev *onewire,void *buff,int size)
{
 int i;
 char *p = (char*)buff; 
 for(i=0;i  p[i++]=ops_onewire_read_byte(onewire);
 return i;
}

2.3.3 写函数

写函数与读函数同理,即以该函数,通过单总线往外设写入数据,至于代码的实现,完全是时序图的实现,无特殊难点。

先实现单字节写函数,再通过调用单字节写函数实现多字节写函数。

/**
  * @brief  单总线写一字节
  * @param  onewire 总线结构体指针
  * @param  data 待写数据
  * @retval 返回读取的数据
*/

static int ops_onewire_write_byte(struct ops_onewire_dev *onewire,char data)
{
 uint8_t i;
 
 for(i=8;i>0;i--)
 {
  onewire->set_sdo(0);
  onewire->delayus(5);
  if(data&0x01)
   onewire->set_sdo(1);
  else
   onewire->set_sdo(0);
  onewire->delayus(65);
  onewire->set_sdo(1);
  onewire->delayus(2);
  data >>= 1;
 }
 return 0;
}

/**
  * @brief  写多字节
  * @param  onewire 总线结构体指针
  * @param  buff 代写数据地址
  * @param  size 数据大小
  * @retval 写入数据大小
*/

int ops_onewire_write(struct ops_onewire_dev *onewire,void *buff,int size)
{
 int i;
 char *p = (char*)buff;
 for(i=0;i {
  if(ops_onewire_write_byte(onewire,p[i]) != 0)
  break;
 }
 return i;
}

至此,onewire(单总线)抽象化完成,此部分代码与硬件层分离,亦可单独作为一个模块,移植到不同平台CPU时,也几乎无需改动。

剩下部分工作则是实现“struct ops_onewire_dev”中的函数指针原型,即可使用一根单总线。

3.onewire 抽象应用

以STM32F1为例,实现上述抽象接口。

3.1 “struct ops_onewire_dev” 实现

此部分即是与硬件相关部分,不同CPU平台改动该部分即可,如从51单片机移植到STM32上。

下面涉及到的IO宏,是对应IO的宏定义,如“ONEWIRE1_PORT”、“ONEWIRE1_PIN”,实际使用的是PC13 IO口。

3.1.1 IO输出

static void gpio_set_sdo(int8_t state)
{
    if (state)
  GPIO_SetBits(ONEWIRE1_PORT,ONEWIRE1_PIN); 
    else
  GPIO_ResetBits(ONEWIRE1_PORT,ONEWIRE1_PIN); 
}

3.1.2 IO输入

static uint8_t gpio_get_sdo(void)
{
    return (GPIO_ReadInputDataBit(ONEWIRE1_PORT,ONEWIRE1_PIN));
}

3.1.3 延时函数

static void gpio_delayus(uint32_t us)
{
#if 1  /* 不用系统延时时,开启 */
    volatile int32_t i;
 
    for (; us > 0; us--)
    {
     i = 30;  //mini 17
        while(i--);
    }
#else
  delayus(us);
#endif
}

3.1 onewire 总线初始化

3.1.1 onewire 抽象相关

第一步:定义一个“struct ops_onewire_dev”结构体类型变量(全局)——onewire1_dev。

struct ops_onewire_dev onewire1_dev;

第二步:实例化“onewire1_dev”中的函数指针。

onewire1_dev.get_sdo = gpio_get_sdo;
onewire1_dev.set_sdo = gpio_set_sdo;
onewire1_dev.delayus = gpio_delayus;

第三步:使用时,通过传入“onewire1_dev”地址(指针)即可。

3.1.2 onewire 基础相关

初始基础部分,与使用的CPU硬件相关,如时钟、IO方向等。

/**
  * @brief  初始化单总线
  * @param  none
  * @retval none
*/

void stm32f1xx_onewire1_init(void)
{
 GPIO_InitTypeDef GPIO_InitStructure;          
 RCC_APB2PeriphClockCmd(ONEWIRE1_RCC,ENABLE);  

 GPIO_InitStructure.GPIO_Pin = ONEWIRE1_PIN;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      
   GPIO_Init(ONEWIRE1_PORT, &GPIO_InitStructure);             
 ONEWIRE1_PORT->BSRR = ONEWIRE1_PIN;      
 
 /* device init */
 onewire1_dev.get_sdo = gpio_get_sdo;
 onewire1_dev.set_sdo = gpio_set_sdo;
 onewire1_dev.delayus = gpio_delayus;
}

4.onewire 使用

经过前面的步骤后,我们已经通过IO口翻转,模拟实现了一根单总线——“onewire1_dev”,以DS18B20为例,调用第一部分中三者接口,实现对DS18B20的操作。

4.1 DS18B20操作

对于DS18B20,不陌生,即是温度传感器,不多赘述,使用的功能主要是作为温度检测,另外还有其内部的唯一序列号会作为同一总线上挂多个DS18B20时的“地址”识别。

亦可把DS18B20的唯一序列号作为模块、产品、通信总线等的唯一标识使用。因此,代码也是主要实现这两个功能。

#include "onewire_hw.h"
#include "ds18b20.h"

static uint8_t ds18b20_start(void)
{    
 char reg;
    ops_onewire_reset(&onewire1_dev);     
      
 reg = 0xcc/* 跳过ROM */
 ops_onewire_write(&onewire1_dev,®,1);
 reg = 0x44/* 温度转换指令 */
 ops_onewire_write(&onewire1_dev,®,1);        
 return 0;
}

/**
  * @brief  读取温度
  * @param  none
  * @retval 温度值,浮点型
*/

float ds18b20_readtemp(void)
{
    uint8_t  tl,th,sign;
 uint16_t reg_temp; 
 char reg;
 float temp;
 
 ds18b20_start();
 ops_onewire_reset(&onewire1_dev); 
 reg = 0xcc;
 ops_onewire_write(&onewire1_dev,®,1); /* 跳过ROM */
 reg = 0xbe;
 ops_onewire_write(&onewire1_dev,®,1); /* 读取RAM */
 ops_onewire_read(&onewire1_dev,&tl,1);  /* 低8位数据 */
 ops_onewire_read(&onewire1_dev,&th,1);   /* 高8位数据 */
 if(th > 7)
 {/* - */
  th = ~th;
  tl = ~tl + 1
  sign = 0;             
 }
 else 
 {/* + */
  sign = 1;   
 }  
 reg_temp = (th<<8) | tl;
 temp = reg_temp * 0.0625f
 if(sign)
 {
  return temp;       
 }
 else 
 {
  return -temp;   
 }
}
 
/**
  * @brief  读唯一序列号
  * @param  rom 返回序列号缓存
  * @retval none
*/

void ds18b20_readrom(char *rom)
{
 uint8_t i;
 char reg;
 
 ops_onewire_reset(&onewire1_dev);
 reg = 0x33;
 ops_onewire_write(&onewire1_dev,®,1);
 for (i = 0;i < 8;i++)
 {
  ops_onewire_read(&onewire1_dev,&rom[i],1);  
 } 
}

至此,完成单总线的抽象分层使用。

5.源码

https://github.com/Prry/drivers-for-mcu

文章来源于网络,版权归原作者所有,如有侵权,请联系删除。



关注【一起学嵌入式】,回复加群进技术交流群。



觉得文章不错,点击“分享”、“”、“在看” 呗!

一起学嵌入式 公众号【一起学嵌入式】,RTOS、Linux编程、C/C++,以及经验分享、行业资讯、物联网等技术知
评论 (0)
  • 引言在语音芯片设计中,输出电路的设计直接影响音频质量与系统稳定性。WT588系列语音芯片(如WT588F02B、WT588F02A/04A/08A等),因其高集成度与灵活性被广泛应用于智能设备。然而,不同型号在硬件设计上存在关键差异,尤其是DAC加功放输出电路的配置要求。本文将从硬件架构、电路设计要点及选型建议三方面,解析WT588F02B与F02A/04A/08A的核心区别,帮助开发者高效完成产品设计。一、核心硬件差异对比WT588F02B与F02A/04A/08A系列芯片均支持PWM直推喇叭
    广州唯创电子 2025-04-01 08:53 145浏览
  • 职场之路并非一帆风顺,从初入职场的新人成长为团队中不可或缺的骨干,背后需要经历一系列内在的蜕变。许多人误以为只需努力工作便能顺利晋升,其实核心在于思维方式的更新。走出舒适区、打破旧有框架,正是让自己与众不同的重要法宝。在这条道路上,你不只需要扎实的技能,更需要敏锐的观察力、不断自省的精神和前瞻的格局。今天,就来聊聊那改变命运的三大思维转变,让你在职场上稳步前行。工作初期,总会遇到各式各样的难题。最初,我们习惯于围绕手头任务来制定计划,专注于眼前的目标。然而,职场的竞争从来不是单打独斗,而是团队协
    优思学院 2025-04-01 17:29 87浏览
  • 升职这件事,说到底不是单纯靠“干得多”或者“喊得响”。你可能也看过不少人,能力一般,甚至没你努力,却升得飞快;而你,日复一日地拼命干活,升职这两个字却始终离你有点远。这种“不公平”的感觉,其实在很多职场人心里都曾经出现过。但你有没有想过,问题可能就藏在一些你“没当回事”的小细节里?今天,我们就来聊聊你升职总是比别人慢,可能是因为这三个被你忽略的小细节。第一:你做得多,但说得少你可能是那种“默默付出型”的员工。项目来了接着干,困难来了顶上去,别人不愿意做的事情你都做了。但问题是,这些事情你做了,却
    优思学院 2025-03-31 14:58 98浏览
  • 据先科电子官方信息,其产品包装标签将于2024年5月1日进行全面升级。作为电子元器件行业资讯平台,大鱼芯城为您梳理本次变更的核心内容及影响:一、标签变更核心要点标签整合与环保优化变更前:卷盘、内盒及外箱需分别粘贴2张标签(含独立环保标识)。变更后:环保标识(RoHS/HAF/PbF)整合至单张标签,减少重复贴标流程。标签尺寸调整卷盘/内盒标签:尺寸由5030mm升级至**8040mm**,信息展示更清晰。外箱标签:尺寸统一为8040mm(原7040mm),提升一致性。关键信息新增新增LOT批次编
    大鱼芯城 2025-04-01 15:02 151浏览
  • 在智能交互设备快速发展的今天,语音芯片作为人机交互的核心组件,其性能直接影响用户体验与产品竞争力。WT588F02B-8S语音芯片,凭借其静态功耗<5μA的卓越低功耗特性,成为物联网、智能家居、工业自动化等领域的理想选择,为设备赋予“听得懂、说得清”的智能化能力。一、核心优势:低功耗与高性能的完美结合超低待机功耗WT588F02B-8S在休眠模式下待机电流仅为5μA以下,显著延长了电池供电设备的续航能力。例如,在电子锁、气体检测仪等需长期待机的场景中,用户无需频繁更换电池,降低了维护成本。灵活的
    广州唯创电子 2025-04-02 08:34 46浏览
  • 提到“质量”这两个字,我们不会忘记那些奠定基础的大师们:休哈特、戴明、朱兰、克劳士比、费根堡姆、石川馨、田口玄一……正是他们的思想和实践,构筑了现代质量管理的核心体系,也深远影响了无数企业和管理者。今天,就让我们一同致敬这些质量管理的先驱!(最近流行『吉卜力风格』AI插图,我们也来玩玩用『吉卜力风格』重绘质量大师画象)1. 休哈特:统计质量控制的奠基者沃尔特·A·休哈特,美国工程师、统计学家,被誉为“统计质量控制之父”。1924年,他提出世界上第一张控制图,并于1931年出版《产品制造质量的经济
    优思学院 2025-04-01 14:02 104浏览
  • 文/Leon编辑/cc孙聪颖‍步入 2025 年,国家进一步加大促消费、扩内需的政策力度,家电国补政策将持续贯穿全年。这一利好举措,为行业发展注入强劲的增长动力。(详情见:2025:消费提振要靠国补还是“看不见的手”?)但与此同时,也对家电企业在战略规划、产品打造以及市场营销等多个维度,提出了更为严苛的要求。在刚刚落幕的中国家电及消费电子博览会(AWE)上,家电行业的竞争呈现出胶着的态势,各大品牌为在激烈的市场竞争中脱颖而出,纷纷加大产品研发投入,积极推出新产品,试图提升产品附加值与市场竞争力。
    华尔街科技眼 2025-04-01 19:49 73浏览
  •        在“软件定义汽车”的时代浪潮下,车载软件的重要性日益凸显,软件在整车成本中的比重逐步攀升,已成为汽车智能化、网联化、电动化发展的核心驱动力。车载软件的质量直接关系到车辆的安全性、可靠性以及用户体验,因此,构建一套科学、严谨、高效的车载软件研发流程,确保软件质量的稳定性和可控性,已成为行业共识和迫切需求。       作为汽车电子系统领域的杰出企业,经纬恒润深刻理解车载软件研发的复杂性和挑战性,致力于为O
    经纬恒润 2025-03-31 16:48 79浏览
  • 引言随着物联网和智能设备的快速发展,语音交互技术逐渐成为提升用户体验的核心功能之一。在此背景下,WT588E02B-8S语音芯片,凭借其创新的远程更新(OTA)功能、灵活定制能力及高集成度设计,成为智能设备语音方案的优选。本文将从技术特性、远程更新机制及典型应用场景三方面,解析该芯片的技术优势与实际应用价值。一、WT588E02B-8S语音芯片的核心技术特性高性能硬件架构WT588E02B-8S采用16位DSP内核,内部振荡频率达32MHz,支持16位PWM/DAC输出,可直接驱动8Ω/0.5W
    广州唯创电子 2025-04-01 08:38 133浏览
  • REACH和RoHS欧盟两项重要的环保法规有什么区别?适用范围有哪些?如何办理?REACH和RoHS是欧盟两项重要的环保法规,主要区别如下:一、核心定义与目标RoHS全称为《关于限制在电子电器设备中使用某些有害成分的指令》,旨在限制电子电器产品中的铅(Pb)、汞(Hg)、镉(Cd)、六价铬(Cr6+)、多溴联苯(PBBs)和多溴二苯醚(PBDEs)共6种物质,通过限制特定材料使用保障健康和环境安全REACH全称为《化学品的注册、评估、授权和限制》,覆盖欧盟市场所有化学品(食品和药品除外),通过登
    张工13144450251 2025-03-31 21:18 102浏览
  • 文/郭楚妤编辑/cc孙聪颖‍不久前,中国发展高层论坛 2025 年年会(CDF)刚刚落下帷幕。本次年会围绕 “全面释放发展动能,共促全球经济稳定增长” 这一主题,吸引了全球各界目光,众多重磅嘉宾的出席与发言成为舆论焦点。其中,韩国三星集团会长李在镕时隔两年的访华之行,更是引发广泛热议。一直以来,李在镕给外界的印象是不苟言笑。然而,在论坛开幕前一天,李在镕却意外打破固有形象。3 月 22 日,李在镕与高通公司总裁安蒙一同现身北京小米汽车工厂。小米方面极为重视此次会面,CEO 雷军亲自接待,小米副董
    华尔街科技眼 2025-04-01 19:39 69浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦