感谢硬汉哥提供的方案H7-TOOL同时采样10路DS18B20实现,带CRC校验,很实用 我们知道,DS18B20 是单总线时序通讯,采用严格的信号时序,以保证数据的完整性。因此如果采用操作系统,逻辑读取DS18B20为了避免时序被操作系统的任务调度,或者其他中断打乱,我们都会采用临界区的形式保护时序。
正常情况下,DS18B20通信的时间并不长,也就几十us。时间长的是18b20的转换过程(750ms)。 那么我们可以再和18B20通讯时关闭中断,通讯完毕立马开启中断,转换的过程可以不用管(时序要求再通讯部分)。这样就可以既不影响18b20数据的读取,一起不影响任务的调度。
但是这样的话,我们读取到的温度值就是上一时刻的转换完成的值了。
并且还需要使用各种DelayMs,DelayUs。在RTOS中非常的不方便,因此我们可以使用定时器加状态图的方式实现数据读取,状态图如下:
该种思想我们可以应用到FPGA中,FPGA操作时也是采用状态转移图实现。
时序
-
读操作
代码:
#include "bsp.h"
#include "param.h"
#define DS18B20_IRQ_HANDLE TIM16_IRQHandler
#define TIM_HARD TIM16
#define TIM_HARD_IRQn TIM16_IRQn
#define DQ_DIR_OUTPUT(ch) EIO_ConfigPort(ch, ES_GPIO_OUT)
#define DQ_DIR_INPUT(ch) EIO_ConfigPort(ch, ES_GPIO_IN)
#define DQ_0(ch) EIO_SetOutLevel(ch, 0)
#define DQ_1(ch) EIO_SetOutLevel(ch, 1)
#define DQ_IS_LOW(ch) (EIO_GetInputLevel(ch) == 0)
#define DS_CHAN_NUM 10
static float s_DS18B20_TempReg[DS_CHAN_NUM];
static uint16_t s_err[DS_CHAN_NUM] = {0};
static void DS18B20_StartTimerIRQ(void);
static void DS18B20_StopTimerIRQ(void);
static volatile uint8_t s_ch = 0;
static volatile uint16_t s_status = 0;
uint8_t DS18B20_ReadTemp(uint8_t _ch, float *_result)
{
uint8_t re = 0;
if (_ch >= DS_CHAN_NUM)
{
return re;
}
EIO_ConfigPort(_ch, ES_GPIO_OUT);
s_err[_ch] = 1;
s_ch = _ch;
s_status = 0;
DS18B20_StartTimerIRQ();
while (s_status != 10)
{
}
DS18B20_StopTimerIRQ();
if (s_err[s_ch] == 1)
{
re = 0;
}
else
{
*_result = (float)s_DS18B20_TempReg[s_ch] / 16;
re = 1;
}
return re;
}
uint8_t DS18B20_CaculCRC(uint8_t *_buf, uint8_t _len)
{
uint8_t crc = 0, i, j;
for (i = 0; i < _len; i++)
{
crc = crc ^ _buf[i];
for (j = 0; j < 8; j++)
{
if (crc & 0x01)
{
crc = (crc >> 1) ^ 0x8C;
}
else
{
crc >>= 1;
}
}
}
return crc;
}
void DS18B20_IRQ_HANDLE(void)
{
static volatile uint16_t s_time = 0;
static volatile uint16_t s_call_ret = 0;
static volatile uint16_t s_write_value = 0;
static volatile uint8_t s_i;
static volatile uint8_t s_read;
static volatile uint8_t s_rx_buf[9];
static volatile uint8_t s_rx_len = 0;
if (TIM_HARD->SR & TIM_IT_UPDATE)
{
TIM_HARD->SR = (uint16_t)~TIM_IT_UPDATE;
}
s_time++;
switch (s_status)
{
case 0:
s_status++;
break;
case 1:
s_call_ret = s_status + 1;
s_status = 100;
break;
case 2:
s_call_ret = s_status + 1;
s_write_value = 0xcc;
s_status = 110;
break;
case 3:
s_call_ret = s_status + 1;
s_write_value = 0x44;
s_status = 110;
break;
case 4:
s_call_ret = s_status + 1;
s_status = 100;
break;
case 5:
s_call_ret = s_status + 1;
s_write_value = 0xcc;
s_status = 110;
break;
case 6:
s_call_ret = s_status + 1;
s_write_value = 0xBE;
s_status = 110;
break;
case 7:
s_call_ret = s_status + 1;
s_status = 120;
break;
case 8:
if (DS18B20_CaculCRC((uint8_t *)s_rx_buf, 8) == s_rx_buf[8] && s_rx_buf[4] == 0x7F)
{
int16_t reg16;
reg16 = (s_rx_buf[1] << 8) + s_rx_buf[0];
s_DS18B20_TempReg[s_ch] = reg16;
s_err[s_ch] = 0;
}
else
{
s_err[s_ch] = 1;
}
s_status++;
break;
case 9:
DS18B20_StopTimerIRQ();
s_status++;
break;
case 10:
;
break;
case 99:
s_err[s_ch] = 1;
s_status = 9;
break;
case 100:
DQ_DIR_OUTPUT(s_ch);
DQ_0(s_ch);
s_time = 0;
s_status++;
break;
case 101:
if (s_time >= 52)
{
DQ_DIR_INPUT(s_ch);
s_time = 0;
s_status++;
}
break;
case 102:
if (s_time >= 6)
{
s_time = 0;
s_status++;
}
if (!DQ_IS_LOW(s_ch))
{
s_time = 0;
s_status++;
}
break;
case 103:
if (DQ_IS_LOW(s_ch))
{
s_time = 0;
s_status++;
}
if (s_time > 8)
{
s_status = 99;
}
break;
case 104:
if (!DQ_IS_LOW(s_ch))
{
s_time = 0;
s_status++;
}
if (s_time > 30)
{
s_status = 99;
}
break;
case 105:
s_status = s_call_ret;
break;
case 110:
s_i = 0;
s_status++;
break;
case 110 + 1:
DQ_DIR_OUTPUT(s_ch);
DQ_0(s_ch);
bsp_DelayUS(2);
if (s_write_value & 0x01)
{
DQ_1(s_ch);
}
else
{
DQ_0(s_ch);
}
s_time = 0;
s_status++;
break;
case 110 + 2:
if (s_time >= 6)
{
DQ_1(s_ch);
s_write_value >>= 1;
if (++s_i >= 8)
{
s_status++;
}
else
{
s_status--;
}
}
break;
case 110 + 3:
s_status = s_call_ret;
break;
case 120:
s_rx_len = 0;
s_status++;
break;
case 120 + 1:
s_i = 0;
s_read = 0;
s_status++;
break;
case 120 + 2:
s_read >>= 1;
DQ_DIR_OUTPUT(s_ch);
DQ_0(s_ch);
bsp_DelayUS(3);
DQ_DIR_INPUT(s_ch);
bsp_DelayUS(3);
if (DQ_IS_LOW(s_ch))
{
;
}
else
{
s_read |= 0x80;
}
s_time = 0;
s_status++;
break;
case 120 + 3:
if (s_time >= 6)
{
if (++s_i >= 8)
{
s_status++;
}
else
{
s_status--;
}
}
break;
case 120 + 4:
s_rx_buf[s_rx_len] = s_read;
if (++s_rx_len >= 9)
{
s_status++;
}
else
{
s_status = 120 + 1;
}
break;
case 120 + 5:
s_status = s_call_ret;
break;
}
}
static void DS18B20_StartTimerIRQ(void)
{
bsp_SetTIMforInt(TIM_HARD, 100*1000, 0, 0);
}
static void DS18B20_StopTimerIRQ(void)
{
bsp_SetTIMforInt(TIM_HARD, 0, 0, 0);
}
|