上一篇文章我讲了如何从零开始通过看数据手册上手一款新接触的芯片(MAX6675温度检测),下面是博客的链接,感兴趣的可以先去看一下。
教你如何上手使用一款新的芯片&精准的温度检测方案MAX6675ISA热电偶到数字转换器-CSDN博客
之前这篇文章只是讲述了原理和步骤,没有讲代码实现部分,今天给大家讲一下主从通信以及代码实现部分。从两方面讲,先说硬件的SPI,再说软件模拟的SPI。
先讲一下SPI的主从通信原理,SPI的时钟信号SCL只能由主机提供,从机是根据主机提供的SCL时钟信号来进行收发数据的,从机不能主动给主机发送数据。MOSI中 M是主机 O是输出 S是从机 I是输入,很好理解这个就是主机输出从机输入,是主机给从机发送数据的,同理MISO是从机给主机发送数据的。当使用的是一主一从的通信方式时,NSS引脚可以直接拉低默认选中从机,如果是一主多从,就要通过NSS来选择要通信的从机设备。
主机发送时钟信号,同时会在MOSI上向从机发送数据,同时还会从MISO上读取从机发送的数据,同理从机在接收到时钟信号时,同步接收数据的同时也会发送数据到主机,所以一个时钟信号主机和从机直接就交换了一个bit的数据。
从MAX6675的数据手册可以看到,它通信时还需要一个CS片选信号,需要一个下降沿来通知从机开始通信,所以我们在做主从机通信是还需要去留意芯片的数据手册中的时序图。
下面以MAX6675通信为例子(16位数据)
可以看到CS先是拉低,表示选中,然后开始传输数据。
根据SPI协议,16位数据为0000 0110 0100 0001
不太清楚SPI通信协议的可以看我另外一篇文章,里面讲的比较详细。
下面是硬件SPI的代码部分,硬件SPI的系统配置这里不做过多的讲解,感兴趣的可以看我的另外一篇文章,里面有较为详细的介绍。硬件SPI+DMA驱动ST7789芯片(超详细讲解)(合宙AIR001)-CSDN博客
根据上面的时序图介绍,我们需要一个CS片选引脚,所以我们先定义一个引脚作为CS使用,并且初始化成推完输出模式,不同的库的初始化函数不一样,下面是我的展示,大家自行修改。
#define MAX6675_CS1_PORT GPIOA
#define MAX6675_CS1_PIN GPIO_PIN_4
#define MAX6675_CS1_SET HAL_GPIO_WritePin(MAX6675_CS1_PORT, MAX6675_CS1_PIN,1);
#define MAX6675_CS1_CLE HAL_GPIO_WritePin(MAX6675_CS1_PORT, MAX6675_CS1_PIN,0);
void max6675_init(void)
{
GPIO_InitTypeDef CS = {.Pin = GPIO_PIN_4,.Mode = GPIO_MODE_OUTPUT_PP,};
__HAL_RCC_GPIOA_CLK_ENABLE();
HAL_GPIO_Init(GPIOA, &CS);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4,1);
}
根据数据手册可知有效位是D14-D3,热电偶检测是D2(开路为1,默认为0)
下面是获取从机(MAX6675芯片)的数据
HAL_SPI_TransmitReceive_DMA(&Spi1Handle, (uint8_t *)TxBuff,(uint8_t *)RxBuff,2);函数是DMA同时读写内容的函数,TxBuff是发送数据的地址,RxBuff是接收数据的地址。
从上面的数据含义可以知道,热电偶检测是D2(开路为1,默认为0),所以如果热电偶开路则直接输出4095。数据的有效位是D14-D3,所以直接右移3位即可得到。
uint8_t TxBuff[2] = {0xFF,0X00};
uint8_t RxBuff[2] = {0,0};
uint16_t max6675_readRawValue(void)
{
uint16_t tmp;
MAX6675_CS1_CLE;
HAL_SPI_TransmitReceive_DMA(&Spi1Handle, (uint8_t *)TxBuff,(uint8_t *)RxBuff,2);
tmp=RxBuff[0];
tmp <<= 8;
tmp |= RxBuff[1];
MAX6675_CS1_SET;
if (tmp & 4)
{
// thermocouple open
tmp = 4095; //未检测到热电偶
}
else
{
tmp = tmp >> 3;
}
return tmp;
}
下面是数据转换成温度的处理,具体关系可以看我另外一篇文章教你如何上手使用一款新的芯片&精准的温度检测方案MAX6675ISA热电偶到数字转换器-CSDN博客
float max6675_readTemperature(void)
{
return (max6675_readRawValue() * 1024.0 / 4096.0);
}
最后是配置串口输出一下数据看是否正确
printf("%f",max6675_readTemperature());
串口输出正常,温度正常,成功实现。
下面是用软件来模拟SPI读取数据
软件模拟SPI读取数据需要配置3个引脚,CS、SCL、SO(MISO)
初始化的时候注意CS、SCL配置成推挽输出,SO要配置成浮空输入,这里不要搞错。
#define MAX6675_CS1_PORT GPIOA
#define MAX6675_CS1_PIN GPIO_PIN_4
#define MAX6675_CS1_SET HAL_GPIO_WritePin(MAX6675_CS1_PORT, MAX6675_CS1_PIN,1);
#define MAX6675_CS1_CLE HAL_GPIO_WritePin(MAX6675_CS1_PORT, MAX6675_CS1_PIN,0);
#define MAX6675_SCL GPIOF
#define MAX6675_SCL_PIN GPIO_PIN_1
#define MAX6675_SCL_SET HAL_GPIO_WritePin(MAX6675_SCL, MAX6675_SCL_PIN,1);
#define MAX6675_SCL_CLE HAL_GPIO_WritePin(MAX6675_SCL, MAX6675_SCL_PIN,0);
#define MAX6675_SO GPIOF
#define MAX6675_SO_PIN GPIO_PIN_0
void max6675_init(void)
{
GPIO_InitTypeDef CS = {.Pin = GPIO_PIN_4,.Mode = GPIO_MODE_OUTPUT_PP,};
__HAL_RCC_GPIOA_CLK_ENABLE();
HAL_GPIO_Init(GPIOA, &CS);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4,1);
GPIO_InitTypeDef SO = {.Pin = MAX6675_SO_PIN,.Mode = GPIO_MODE_INPUT,};
__HAL_RCC_GPIOF_CLK_ENABLE();
HAL_GPIO_Init(MAX6675_SO, &SO);
HAL_GPIO_WritePin(MAX6675_SO, MAX6675_SO_PIN,0);
GPIO_InitTypeDef SCL = {.Pin = MAX6675_SCL_PIN,.Mode = GPIO_MODE_OUTPUT_PP,};
__HAL_RCC_GPIOF_CLK_ENABLE();
HAL_GPIO_Init(MAX6675_SCL, &SCL);
HAL_GPIO_WritePin(MAX6675_SCL, MAX6675_SCL_PIN,0);
}
下面是软件模拟的读取数据函数,现将CS拉低,然后用引脚的高低电平模拟出SCL的时钟信号,同时将SO上的数据逐个读出 。
uint16_t read(void)
{
uint16_t count;
MAX6675_CS1_CLE;
//HAL_Delay(1);
for(int i=0;i<16;i++)
{
MAX6675_SCL_SET;
count|=HAL_GPIO_ReadPin(MAX6675_SO,MAX6675_SO_PIN);
if(i<=14)
count=count<<1;
MAX6675_SCL_CLE;
HAL_Delay(1);
}
//HAL_Delay(1);
MAX6675_CS1_SET;
if (count & 4)
{
// thermocouple open
count = 4095; //未检测到热电偶
}
else
{
count = count >> 3;
}
return count;
}
下面是温度转换函数
float get_tem()
{
return(read()* 1024.0 / 4096.0);
}
最后打印出数据,验证代码
printf("%f",get_tem());
成功实现,完结撒花。
我最近建了一个嵌入式的QQ交流群,感兴趣的可以进群了解一下,我会在群里分享一些常用的代码封装,以及一些项目的源码。QQ群讨论也是完全开放,只要不打广告大家可以就嵌入式尽情的沟通和交流,大家对文章中的内容有疑问也可以在群中提出,有空会尽我所能给大家一些帮助。QQ群号:643408467