单总线的抽象分层!

嵌入式大杂烩 2023-07-24 20:28

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

本文来源网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。

往期推荐

《嵌入式Linux驱动大全》

在公众号聊天界面回复1024,可获取嵌入式资源

嵌入式大杂烩 专注于嵌入式技术,包括但不限于C/C++、嵌入式、物联网、Linux等编程学习笔记,同时,内包含大量的学习资源。欢迎关注,一同交流学习,共同进步!
评论 (0)
  • 文/Leon编辑/cc孙聪颖‍2023年,厨电行业在相对平稳的市场环境中迎来温和复苏,看似为行业增长积蓄势能。带着对市场向好的预期,2024 年初,老板电器副董事长兼总经理任富佳为企业定下双位数增长目标。然而现实与预期相悖,过去一年,这家老牌厨电企业不仅未能达成业绩目标,曾提出的“三年再造一个老板电器”愿景,也因市场下行压力面临落空风险。作为“企二代”管理者,任富佳在掌舵企业穿越市场周期的过程中,正面临着前所未有的挑战。4月29日,老板电器(002508.SZ)发布了2024年年度报告及2025
    华尔街科技眼 2025-04-30 12:40 74浏览
  • 贞光科技代理品牌紫光国芯的车规级LPDDR4内存正成为智能驾驶舱的核心选择。在汽车电子国产化浪潮中,其产品以宽温域稳定工作能力、优异电磁兼容性和超长使用寿命赢得市场认可。紫光国芯不仅确保供应链安全可控,还提供专业本地技术支持。面向未来,紫光国芯正研发LPDDR5车规级产品,将以更高带宽、更低功耗支持汽车智能化发展。随着智能网联汽车的迅猛发展,智能驾驶舱作为人机交互的核心载体,对处理器和存储器的性能与可靠性提出了更高要求。在汽车电子国产化浪潮中,贞光科技代理品牌紫光国芯的车规级LPDDR4内存凭借
    贞光科技 2025-04-28 16:52 166浏览
  • 浪潮之上:智能时代的觉醒    近日参加了一场课题的答辩,这是医疗人工智能揭榜挂帅的国家项目的地区考场,参与者众多,围绕着医疗健康的主题,八仙过海各显神通,百花齐放。   中国大地正在发生着激动人心的场景:深圳前海深港人工智能算力中心高速运转的液冷服务器,武汉马路上自动驾驶出租车穿行的智慧道路,机器人参与北京的马拉松竞赛。从中央到地方,人工智能相关政策和消息如雨后春笋般不断出台,数字中国的建设图景正在智能浪潮中徐徐展开,战略布局如同围棋
    广州铁金刚 2025-04-30 15:24 66浏览
  • 文/郭楚妤编辑/cc孙聪颖‍越来越多的企业开始蚕食动力电池市场,行业“去宁王化”态势逐渐明显。随着这种趋势的加强,打开新的市场对于宁德时代而言至关重要。“我们不希望被定义为电池的制造者,而是希望把自己称作新能源产业的开拓者。”4月21日,在宁德时代举行的“超级科技日”发布会上,宁德时代掌门人曾毓群如是说。随着宁德时代核心新品骁遥双核电池的发布,其搭载的“电电增程”技术也走进业界视野。除此之外,经过近3年试水,宁德时代在换电业务上重资加码。曾毓群认为换电是一个重资产、高投入、长周期的产业,涉及的利
    华尔街科技眼 2025-04-28 21:55 72浏览
  • 4月22日下午,备受瞩目的飞凌嵌入式「2025嵌入式及边缘AI技术论坛」在深圳深铁皇冠假日酒店盛大举行,此次活动邀请到了200余位嵌入式技术领域的技术专家、企业代表和工程师用户,共享嵌入式及边缘AI技术的盛宴!1、精彩纷呈的展区产品及方案展区是本场活动的第一场重头戏,从硬件产品到软件系统,从企业级应用到高校教学应用,都吸引了现场来宾的驻足观看和交流讨论。全产品矩阵展区展示了飞凌嵌入式丰富的产品线,从嵌入式板卡到工控机,从进口芯片平台到全国产平台,无不体现出飞凌嵌入式在嵌入式主控设备研发设计方面的
    飞凌嵌入式 2025-04-28 14:43 123浏览
  • 你是不是也有在公共场合被偷看手机或笔电的经验呢?科技时代下,不少现代人的各式机密数据都在手机、平板或是笔电等可携式的3C产品上处理,若是经常性地需要在公共场合使用,不管是工作上的机密文件,或是重要的个人信息等,民众都有防窃防盗意识,为了避免他人窥探内容,都会选择使用「防窥保护贴片」,以防止数据外泄。现今市面上「防窥保护贴」、「防窥片」、「屏幕防窥膜」等产品就是这种目的下产物 (以下简称防窥片)!防窥片功能与常见问题解析首先,防窥片最主要的功能就是用来防止他人窥视屏幕上的隐私信息,它是利用百叶窗的
    百佳泰测试实验室 2025-04-30 13:28 76浏览
  • 网约车,真的“饱和”了?近日,网约车市场的 “饱和” 话题再度引发热议。多地陆续发布网约车风险预警,提醒从业者谨慎入局,这背后究竟隐藏着怎样的市场现状呢?从数据来看,网约车市场的“过剩”现象已愈发明显。以东莞为例,截至2024年12月底,全市网约车数量超过5.77万辆,考取网约车驾驶员证的人数更是超过13.48万人。随着司机数量的不断攀升,订单量却未能同步增长,导致单车日均接单量和营收双双下降。2024年下半年,东莞网约出租车单车日均订单量约10.5单,而单车日均营收也不容乐
    用户1742991715177 2025-04-29 18:28 106浏览
  • 随着电子元器件的快速发展,导致各种常见的贴片电阻元器件也越来越小,给我们分辨也就变得越来越难,下面就由smt贴片加工厂_安徽英特丽就来告诉大家如何分辨的SMT贴片元器件。先来看看贴片电感和贴片电容的区分:(1)看颜色(黑色)——一般黑色都是贴片电感。贴片电容只有勇于精密设备中的贴片钽电容才是黑色的,其他普通贴片电容基本都不是黑色的。(2)看型号标码——贴片电感以L开头,贴片电容以C开头。从外形是圆形初步判断应为电感,测量两端电阻为零点几欧,则为电感。(3)检测——贴片电感一般阻值小,更没有“充放
    贴片加工小安 2025-04-29 14:59 121浏览
  • 在CAN总线分析软件领域,当CANoe不再是唯一选择时,虹科PCAN-Explorer 6软件成为了一个有竞争力的解决方案。在现代工业控制和汽车领域,CAN总线分析软件的重要性不言而喻。随着技术的进步和市场需求的多样化,单一的解决方案已无法满足所有用户的需求。正是在这样的背景下,虹科PCAN-Explorer 6软件以其独特的模块化设计和灵活的功能扩展,为CAN总线分析领域带来了新的选择和可能性。本文将深入探讨虹科PCAN-Explorer 6软件如何以其创新的模块化插件策略,提供定制化的功能选
    虹科汽车智能互联 2025-04-28 16:00 136浏览
  • 一、gao效冷却与控温机制‌1、‌冷媒流动设计‌采用低压液氮(或液氦)通过毛细管路导入蒸发器,蒸汽喷射至样品腔实现快速冷却,冷却效率高(室温至80K约20分钟,至4.2K约30分钟)。通过控温仪动态调节蒸发器加热功率,结合温度传感器(如PT100铂电阻或Cernox磁场不敏感传感器),实现±0.01K的高精度温度稳定性。2、‌宽温区覆盖与扩展性‌标准温区为80K-325K,通过降压选件可将下限延伸至65K(液氮模式)或4K(液氦模式)。可选配475K高温模块,满足材料在ji端温度下的性能测试需求
    锦正茂科技 2025-04-30 13:08 71浏览
  • 在智能硬件设备趋向微型化的背景下,语音芯片方案厂商针对小体积设备开发了多款超小型语音芯片方案,其中WTV系列和WT2003H系列凭借其QFN封装设计、高性能与高集成度,成为微型设备语音方案的理想选择。以下从封装特性、功能优势及典型应用场景三个方面进行详细介绍。一、超小体积封装:QFN技术的核心优势WTV系列与WT2003H系列均提供QFN封装(如QFN32,尺寸为4×4mm),这种封装形式具有以下特点:体积紧凑:QFN封装通过减少引脚间距和优化内部结构,显著缩小芯片体积,适用于智能门铃、穿戴设备
    广州唯创电子 2025-04-30 09:02 105浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦