STM32F103C8T6驱动MAX31865获取PT1000温度数据

一、MAX31865

高度集成降低系统成本、简化设计并缩短设计周期

  • 简便的RTD铂电阻至数字转换器
  • 支持100Ω至1kΩ (0°C时)铂电阻RTD (PT100至PT1000)
  • 兼容2线、3线和4线传感器连接
  • SPI兼容接口 高精度设计方便满足误差预算
  • 15位ADC分辨率;标称温度分辨率为0.03125°C (随RTD的非线性变化)
  • 整个工作条件下,总精度保持在最高0.5°C (0.05%满量程)
  • 全差分VREF输入
  • 转换时间:21ms (最大值)

集成故障检测提高系统可靠性

  • ±45V输入保护
  • 故障检测(RTD开路、RTD短路到量程范围以外的电压或RTD元件短路)

1.重要寄存器

Configuration Register (读00h,写80h)

BIAS (D7)

当没有执行转换时,可以禁用 VBIAS 以降低功耗。将 1 写入此位以在开始单个 (1-Shot) 转换之前启用 VBIAS。当选择自动(连续)转换模式时,VBIAS 保持连续开启。

Conversion Mode (D6)

将 1 写入该位以选择自动转换模式,在该模式下,转换以 50/60Hz 的速率连续进行。将 0 写入该位以退出自动转换模式并进入 “Normally Off” 模式。可以从此模式启动 1-shot 转换。

1-Shot (D5)

当转换模式设置为 “Normally Off” 时,将 1 写入该位以开始转换。这会导致发生单个电阻转换。当 CS 向该位写入 1 后变为高电平时,将触发转换。请注意,如果执行多字节写入,则当 CS 在事务结束时变为高电平时,将触发转换。如果 VBIAS 导通 (由配置寄存器选择),则当 CS 变为高电平并开始转换时,对 RTD 电压进行采样。注意,如果 VBIAS 关闭(以减少转换之间的电源电流),则 RTDIN 输入端的任何滤波电容都需要充电,然后才能执行精确的转换。因此,请启用 VBIAS 并等待输入 RC 网络的至少 10.5 个时间常数加上额外的 1 毫秒,然后再开始转换。请注意,在 60Hz 滤波器模式下,单次转换大约需要 52 毫秒,在 50Hz 滤波器模式下需要大约 62.5 毫秒才能完成。1-Shot 是一个自清除位。

3-Wire (D4)

当使用 3 线 RTD 连接时,将 1 写入此位。在这种模式下,从 (RTDIN+ - RTDIN-) 中减去 FORCE+ 和 RTDIN+ 之间的电压,以补偿因使用 FORCE 和 RTDIN- 连接使用单线引起的 IR 误差。当使用 2 线或 4 线连接时,将 0 写入此位。

Fault Detection Cycle (D3:D2)

主站启动的故障检测周期有两种操作模式,手动和自动模式定时。如果外部 RTD 接口电路包括一个时间常数大于 100Fs 的输入滤波器,则应在手动模式操作中控制故障检测周期时序。故障检测周期通过进行以下电压比较并在 Fault Status Register 中设置相关位来检查三个故障:
1) REFIN- 处的电压是否大于 85% x VBIAS?(故障状态寄存器位 D5)
2)当 FORCE- 输入开关打开时,REFIN- 处的电压是否小于 85% x VBIAS?(故障状态寄存器位 D4)
3)当 FORCE- 输入开关打开时,RTDIN- 的电压是否小于 85% x VBIAS?(故障状态寄存器位 D3)

Fault Status Clear (D1)

向该位写入 1,同时向位 D5、D3 和 D2 写入 0,以将故障状态寄存器中的所有故障状态位 (D[7:2]) 返回为 0。请注意,如果过压/欠压故障仍然存在,则故障寄存器中的位 D2 和 RTD LSB 寄存器中的位 D0 可以在复位后立即再次设置。故障状态清除位 D1,自清除为 0。

50/60Hz (D0)

此位选择噪声抑制滤波器的陷波频率。将 0 写入该位以拒绝 60Hz 及其谐波;将 1 写入此位以拒绝 50Hz 及其谐波。注意:在自动转换模式下,请勿更改陷波频率。

RTD Resistance Registers (01h-02h)

两个 8 位寄存器 RTD MSB 和 RTD LSB 包含 RTD 电阻数据。数据格式如表 4 所示。数据格式就是 RTD 电阻与基准电阻的 15 位比。RTD LSB寄存器的D0是一个Fault位,指示是否检测到任何RTD故障。

Fault Status Register (07h)

Fault Status 寄存器锁存任何检测到的 fault bit;将 1 写入 Configuration Register 中的 Fault Status Clear 位,将所有 Fault Status 位返回为 0。

2.SPI时序

CS信号低电平有效,CLK信号空闲时为高电平(根据手册18页,空闲时低电平也可),第二个数据沿锁定数据。

3.电气特性

二、驱动

代码

bsp_max31865.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#ifndef __BSP_31865_H
#define __BSP_31865_H
#include "stm32f10x.h"

#define MAX31865_INITVAL 0xC1 //0xC1: 2、4线模式 , 0xD1: 3线模式
#define PT1000_Resistance_Ref 4020 //基准电阻阻值


typedef struct _MAX31865_ {
float temp_out;
uint8_t status;
} MAX31865;


/*---------SPI2 Master配置Start--------*/
#define M_SPI SPI2
#define M_SPI_APBxClock_FUN RCC_APB1PeriphClockCmd
#define M_SPI_CLK RCC_APB1Periph_SPI2

//中断
#define M_SPI_IRQ SPI2_IRQn
#define M_SPI_IRQHandler SPI2_IRQHandler

//CS(NSS)引脚 片选选普通 GPIO 即可
#define M_SPI_CS_APBxClock_FUN RCC_APB2PeriphClockCmd
#define M_SPI_CS_CLK RCC_APB2Periph_GPIOB
#define M_SPI_CS_PORT GPIOB
#define M_SPI_CS_PIN GPIO_Pin_12

//SCK 引脚
#define M_SPI_SCK_APBxClock_FUN RCC_APB2PeriphClockCmd
#define M_SPI_SCK_CLK RCC_APB2Periph_GPIOB
#define M_SPI_SCK_PORT GPIOB
#define M_SPI_SCK_PIN GPIO_Pin_13

//MISO 引脚
#define M_SPI_MISO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define M_SPI_MISO_CLK RCC_APB2Periph_GPIOB
#define M_SPI_MISO_PORT GPIOB
#define M_SPI_MISO_PIN GPIO_Pin_14

//MOSI 引脚
#define M_SPI_MOSI_APBxClock_FUN RCC_APB2PeriphClockCmd
#define M_SPI_MOSI_CLK RCC_APB2Periph_GPIOB
#define M_SPI_MOSI_PORT GPIOB
#define M_SPI_MOSI_PIN GPIO_Pin_15

#define M_SPI_CS_LOW() GPIO_ResetBits(M_SPI_CS_PORT, M_SPI_CS_PIN)
#define M_SPI_CS_HIGH() GPIO_SetBits(M_SPI_CS_PORT, M_SPI_CS_PIN)
/*---------SPI2 Master配置End--------*/

//DRDP引脚
#define MAX31865DRDP_PIN GPIO_Pin_5
#define MAX31865DRDP_PORT GPIOB
#define MAX31865DRDP_CLK RCC_APB2Periph_GPIOB
#define MAX31865DRDP_APBxClock_FUN RCC_APB2PeriphClockCmd
//读DRDP引脚电平状态
#define ReadPIN_DRDP (MAX31865DRDP_PORT->IDR&MAX31865DRDP_PIN)



void MAX31865Init(void);
//u8 CSCheck(void);
u16 SPISendByte(u8 add,u8 byte);
//获取传感器数据
MAX31865 *GetSensorTemp(void);

#endif

bsp_max31865.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include "bsp_max31865.h"

// SPI标志位超时时间
#define SPIT_FLAG_TIMEOUT ((uint32_t)0x1000)

typedef union _UU16_
{
uint16_t U16;
int16_t S16;
uint8_t vU8[2];
int8_t vS8[2];
} _UU16;

static MAX31865 tmpValue = {0};
static __IO uint32_t SPITimeoutCNT = SPIT_FLAG_TIMEOUT;

// 两次cs片选之间需要400nm
static void HardDelay(void)
{
uint8_t delayTick = 0;
while (delayTick < 128)
delayTick++;
}

void MAX31865Init(void)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
// 初始化SPI
/* 使能SPI时钟 */
M_SPI_APBxClock_FUN(M_SPI_CLK, ENABLE);

/* 使能SPI引脚相关的时钟 */
M_SPI_CS_APBxClock_FUN(M_SPI_CS_CLK | M_SPI_SCK_CLK | M_SPI_MISO_PIN | M_SPI_MOSI_PIN, ENABLE);

/* 配置SPI的 CS引脚,普通IO即可 */
GPIO_InitStructure.GPIO_Pin = M_SPI_CS_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(M_SPI_CS_PORT, &GPIO_InitStructure);

/* 配置SPI的 SCK引脚*/
GPIO_InitStructure.GPIO_Pin = M_SPI_SCK_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(M_SPI_SCK_PORT, &GPIO_InitStructure);

/* 配置SPI的 MISO引脚*/
GPIO_InitStructure.GPIO_Pin = M_SPI_MISO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(M_SPI_MISO_PORT, &GPIO_InitStructure);

/* 配置SPI的 MOSI引脚*/
GPIO_InitStructure.GPIO_Pin = M_SPI_MOSI_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(M_SPI_MOSI_PORT, &GPIO_InitStructure);

M_SPI_CS_HIGH();

/* SPI 模式配置 */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; // APB2是32M的时候 SPI主频0.5M
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(M_SPI, &SPI_InitStructure);

/* 使能 SPI */
SPI_Cmd(M_SPI, ENABLE);

/* 使能DRDP引脚相关的时钟 */
MAX31865DRDP_APBxClock_FUN(MAX31865DRDP_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = MAX31865DRDP_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(MAX31865DRDP_PORT, &GPIO_InitStructure);

SPISendByte(0x80, MAX31865_INITVAL); // 初始化
HardDelay();
}

u16 SPISendByte(u8 add, u8 byte)
{
uint16_t tmp16 = add << 8;
tmp16 = tmp16 | byte;

M_SPI_CS_LOW(); // cs引脚变成低电平
SPITimeoutCNT = SPIT_FLAG_TIMEOUT;
/* 等待发送缓冲区为空,TXE事件 */
while (SPI_I2S_GetFlagStatus(M_SPI, SPI_I2S_FLAG_TXE) == RESET)
{
if ((SPITimeoutCNT--) == 0)
{
M_SPI_CS_HIGH();
return 255;
}
}

/* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
SPI_I2S_SendData(M_SPI, tmp16);

SPITimeoutCNT = SPIT_FLAG_TIMEOUT;
/* 等待接收缓冲区非空,RXNE事件 */
while (SPI_I2S_GetFlagStatus(M_SPI, SPI_I2S_FLAG_RXNE) == RESET)
{
if ((SPITimeoutCNT--) == 0)
{
M_SPI_CS_HIGH();
return 255;
}
}
M_SPI_CS_HIGH();
/* 读取数据寄存器,获取接收缓冲区数据 */
return SPI_I2S_ReceiveData(M_SPI);
}


static uint8_t r00 = 0, r01 = 0, r02 = 0, r07 = 0;
static int16_t r01_2 = 0;
static float R_RTD = 0;

MAX31865 *GetSensorTemp(void)
{
_UU16 tmpReg;
r00 = SPISendByte(0x00, 0xFF);
HardDelay();
if (r00 != MAX31865_INITVAL)
{
SPISendByte(0x80, MAX31865_INITVAL);
HardDelay();
tmpValue.status = 0xFF;
return &tmpValue;
}

if (ReadPIN_DRDP != 0) // 高电平 没有转换完毕
return &tmpValue;

r01 = SPISendByte(0x01, 0xFF);
HardDelay();
r02 = SPISendByte(0x02, 0xFF);
HardDelay();
r07 = SPISendByte(0x07, 0xFF);
HardDelay();

if (r07 != 0) // 出错了
{
SPISendByte(0x80, 0xC7);
HardDelay();
}
tmpReg.vU8[1] = r01;
tmpReg.vU8[0] = r02;
r01_2 = tmpReg.U16 >> 1;

R_RTD = (r01_2 / 32768.0f) * PT1000_Resistance_Ref;
tmpValue.status = r07;
if (tmpValue.status == 0) // 状态为0才进行平均
tmpValue.temp_out = 0.000011185431f * R_RTD * R_RTD + 0.23292227f * R_RTD - 244.17129f;
return &tmpValue;
}

调用方法

main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "bsp_max31865.h"
MAX31865 *_Sen_Max31855;
int main(void)
{
HSI_SetSysClock(RCC_PLLMul_16);
MAX31865Init();
...
while (1)
{
_Sen_Max31855 = GetSensorTemp();
delay_ms(1000);
}
}