性无码一区二区三区在线观看,少妇被爽到高潮在线观看,午夜精品一区二区三区,无码中文字幕人妻在线一区二区三区,无码精品国产一区二区三区免费

嵌軟開發(fā)思維之基于單總線的數(shù)據(jù)抽象實例

1.onewire(單總線) 是DALLAS公司推出的外圍串行擴展總線技術(shù)總線,顧名思義,它是采用一根信號線進行通信,既傳輸時鐘信號又傳輸數(shù)據(jù),而且能夠進行雙向通信,具有節(jié)省I/O口線、資源結(jié)構(gòu)簡單、成本低廉、便于總線擴展和維護等諸多優(yōu)點。

常用到單總線的器件,一般是溫度傳感器、EEPROM、唯一序列號芯片等,如DS18B20、DS2431。

在使用單總線時,往往很少CPU會提供硬件單總線,幾乎都是根據(jù)單總線標準的時序圖,通過普通IO翻轉(zhuǎn)模擬實現(xiàn)單總線。而在模式實現(xiàn)時序圖的過程中,需要根據(jù)CPU時鐘頻率等條件進行時序時間計算,如果更換CPU后,需要重新計算時序時間,如果時序代碼和器件外設(shè)控制代碼集成在一起,則代碼改動比較大。

或者同一CPU需要模擬多根單總線時,傳統(tǒng)的“復制”方式使得程序顯得累贅,還增加ROM占用空間。因此,可以利用“函數(shù)指針”的方式,將時序部分抽象出來,達到“復用”代碼的效果,減少重復代碼編寫。

2.onewire 抽象

2.1 onewire 結(jié)構(gòu)體

onewire結(jié)構(gòu)體主要是對與CPU底層相關(guān)的操作抽象分離,調(diào)用時只需將該結(jié)構(gòu)體地址(指針)作為函數(shù)入口參數(shù),通過該指針實現(xiàn)對底層函數(shù)的回調(diào)。該結(jié)構(gòu)體我們命名為“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,包括時鐘和數(shù)據(jù)。 

2)get_sdo:IO輸入1bit,包括時鐘和數(shù)據(jù)。

3)delayus:時序延時函數(shù),根據(jù)CPU頻率進行計算。

回調(diào)函數(shù)相關(guān)可以了解一下

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)分別為復位函數(shù)、讀函數(shù)、寫函數(shù)。 

2)入口首參數(shù)為“struct ops_onewire_dev”結(jié)構(gòu)體指針,此部分就是硬件層相關(guān),需要后期初始化的. 

3)其余入口參數(shù)易于理解,讀/寫緩存及數(shù)據(jù)大小。

2.3 onewire 抽象接口實現(xiàn)

分別實現(xiàn)上述三者函數(shù)接口。

2.3.1 復位函數(shù)

復位函數(shù),在單總線初始化外設(shè)器件時需要用到,用于判斷總線與器件是否通信上,類似“握手”的動作。如圖,為DS18B20的復位時序圖,以下與單總線相關(guān)的時序圖,都是以DS18B20為例,因為此芯片為單總線應用的經(jīng)典。

根據(jù)時序圖,實現(xiàn)復位函數(shù)。

/**
  * @brief  單總線復位時序
  * @param  onewire 總線結(jié)構(gòu)體指針
  * @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 讀函數(shù)

讀函數(shù)即以該函數(shù),通過單總線從外設(shè)上讀取數(shù)據(jù),至于代碼的實現(xiàn),完全是時序圖的實現(xiàn),無特殊難點。先實現(xiàn)單字節(jié)讀函數(shù),再通過調(diào)用單字節(jié)讀函數(shù)實現(xiàn)多字節(jié)讀函數(shù)。

/**
  * @brief  單總線讀取一字節(jié)數(shù)據(jù)
  * @param  onewire 總線結(jié)構(gòu)體指針
  * @retval 返回讀取的數(shù)據(jù)
*/
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  讀取多字節(jié)
  * @param  onewire 總線結(jié)構(gòu)體指針
  * @param  buff 存放數(shù)據(jù)緩存
  * @param  size 數(shù)據(jù)大小
  * @retval 返回讀取到的數(shù)據(jù)大小
*/
int ops_onewire_read(struct ops_onewire_dev *onewire,void *buff,int size)
{
 int i;
 char *p = (char*)buff; 
 for(i=0;i<size;i++)
  p[i++]=ops_onewire_read_byte(onewire);
 return i;
}

2.3.3 寫函數(shù)

寫函數(shù)與讀函數(shù)同理,即以該函數(shù),通過單總線往外設(shè)寫入數(shù)據(jù),至于代碼的實現(xiàn),完全是時序圖的實現(xiàn),無特殊難點。先實現(xiàn)單字節(jié)寫函數(shù),再通過調(diào)用單字節(jié)寫函數(shù)實現(xiàn)多字節(jié)寫函數(shù)。

/**
  * @brief  單總線寫一字節(jié)
  * @param  onewire 總線結(jié)構(gòu)體指針
  * @param  data 待寫數(shù)據(jù)
  * @retval 返回讀取的數(shù)據(jù)
*/
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  寫多字節(jié)
  * @param  onewire 總線結(jié)構(gòu)體指針
  * @param  buff 代寫數(shù)據(jù)地址
  * @param  size 數(shù)據(jù)大小
  * @retval 寫入數(shù)據(jù)大小
*/
int ops_onewire_write(struct ops_onewire_dev *onewire,void *buff,int size)
{
 int i;
 char *p = (char*)buff;
 for(i=0;i<size;i++)
 {
  if(ops_onewire_write_byte(onewire,p[i]) != 0)
  break;
 }
 return i;
}

至此,onewire(單總線)抽象化完成,此部分代碼與硬件層分離,亦可單獨作為一個模塊,移植到不同平臺CPU時,也幾乎無需改動。剩下部分工作則是實現(xiàn)“struct ops_onewire_dev”中的函數(shù)指針原型,即可使用一根單總線。

3.onewire 抽象應用

以STM32F1為例,實現(xiàn)上述抽象接口。

3.1 “struct ops_onewire_dev” 實現(xiàn)

此部分即是與硬件相關(guān)部分,不同CPU平臺改動該部分即可,如從51單片機移植到STM32上。下面涉及到的IO宏,是對應IO的宏定義,如“ONEWIRE1_PORT”、“ONEWIRE1_PIN”,實際使用的是PC13 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輸入3.1.2 IO輸入

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

3.1.1 IO輸出3.1.3 延時函數(shù)

static void gpio_delayus(uint32_t us)
{
#if 1  /* 不用系統(tǒng)延時時,開啟 */
    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 抽象相關(guān)

第一步:定義一個“struct ops_onewire_dev”結(jié)構(gòu)體類型變量(全局)——onewire1_dev。

struct ops_onewire_dev onewire1_dev;

第二步:實例化“onewire1_dev”中的函數(shù)指針。

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 基礎(chǔ)相關(guān)

初始基礎(chǔ)部分,與使用的CPU硬件相關(guān),如時鐘、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 使用

經(jīng)過前面的步驟后,我們已經(jīng)通過IO口翻轉(zhuǎn),模擬實現(xiàn)了一根單總線——“onewire1_dev”,以DS18B20為例,調(diào)用第一部分中三者接口,實現(xiàn)對DS18B20的操作。

4.1 DS18B20操作

對于DS18B20,不陌生,即是溫度傳感器,不多贅述,使用的功能主要是作為溫度檢測,另外還有其內(nèi)部的唯一序列號會作為同一總線上掛多個DS18B20時的“地址”識別。

亦可把DS18B20的唯一序列號作為模塊、產(chǎn)品、通信總線等的唯一標識使用。因此,代碼也是主要實現(xiàn)這兩個功能。

#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,&reg,1);
 reg = 0x44; /* 溫度轉(zhuǎn)換指令 */
 ops_onewire_write(&onewire1_dev,&reg,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,&reg,1); /* 跳過ROM */
 reg = 0xbe;
 ops_onewire_write(&onewire1_dev,&reg,1); /* 讀取RAM */
 ops_onewire_read(&onewire1_dev,&tl,1);  /* 低8位數(shù)據(jù) */
 ops_onewire_read(&onewire1_dev,&th,1);   /* 高8位數(shù)據(jù) */
 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,&reg,1);
 for (i = 0;i < 8;i++)
 {
  ops_onewire_read(&onewire1_dev,&rom[i],1);  
 } 
}

聲明:本內(nèi)容為作者獨立觀點,不代表電子星球立場。未經(jīng)允許不得轉(zhuǎn)載。授權(quán)事宜與稿件投訴,請聯(lián)系:editor@netbroad.com
覺得內(nèi)容不錯的朋友,別忘了一鍵三連哦!
贊 4
收藏 1
關(guān)注 210
成為作者 賺取收益
全部留言
0/200
成為第一個和作者交流的人吧