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頻率進行計算。 回調函數相關文章:C語言技巧之回調函數
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); externintops_onewire_write(structops_onewire_dev*onewire,void*buff,intsize);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
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
至此,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.2onewire 總線初始化
3.2.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.2.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);
}
}
至此,完成單總線的抽象分層使用。
審核編輯:劉清
-
溫度傳感器
+關注
關注
48文章
3210瀏覽量
163080 -
STM32
+關注
關注
2309文章
11162瀏覽量
373406 -
EEPROM
+關注
關注
9文章
1137瀏覽量
86025 -
DS2431
+關注
關注
0文章
4瀏覽量
8608 -
OneWire
+關注
關注
0文章
3瀏覽量
1067
原文標題:STM32基于onewire單總線的數據抽象實例
文章出處:【微信號:c-stm32,微信公眾號:STM32嵌入式開發】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
單總線技術的原理是什么?
單總線協議時序對應代碼
DS18B20芯片用法及代碼解析
單總線測溫系統
單總線溫度監測網絡的設計與實現
1-wire單總線的基本原理
基于單總線的冷庫溫度監控系統設計
單總線協議詳解
單總線優缺點
"藍橋杯單片機設計與開發-底層驅動代碼編寫(iic,onewire,ds1302)"
Onewire單總線驅動DS18B20讀取溫度
STM32基于onewire單總線的數據抽象實例簡析
評論