STM32F1基于HAL库与AD7705、AD7706通信(SPI)
文章目录
前言
使用STM32F1和一个AD7705和一个AD7706进行通信,网上参考内容很多,一定要参考英文手册
引言
SPI 是英语 Serial Peripheral interface 缩写,顾名思义就是串行外围设备接口

SPI 的引脚信息:
MISO(Master In / Slave Out)主设备数据输入,从设备数据输出。
MOSI(Master Out / Slave In)主设备数据输出,从设备数据输入。
SCLK(Serial Clock)时钟信号,由主设备产生。
CS(Chip Select)从设备片选信号,由主设备产生。
SPI 的工作原理:
在主机和从机都有一个串行移位寄存器,主机通过向它的 SPI 串行寄存器写入一个字节来发起一次传输。串行移位寄存器通过 MOSI 信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过 MISO 信号线返回给主机。这样,两个移位寄存器中的内容就被交换。外设的写操作和读操作是同步完成的。如果只是进行写操作,主机只需忽略接收到的字节。反之,若主机要读取从机的一个字节,就必须发送一个空字节引发从机传输。
。 假如空闲状态是高电平, CPOL = 1;若空闲状态时低电平,那么 CPOL = 0。
CPHA,详称 Clock Phase,就是时钟相位。在这里先科普一下数据传输的常识: 同步通信时,数据的变化和采样都是在时钟边沿上进行的,每一个时钟周期都会有上升沿和下降沿两个边沿,那么数据的变化和采样就分别安排在两个不同的边沿,由于数据在产生和到它稳定是需要一定的时间,那么假如我们在第 1 个边沿信号把数据输出了,从机只能从第 2 个边沿信号去采样这个数据。
在2.2中SPI初始化中配置相关参数
一、AD7705、AD7706
1.1 引脚排列

当引用一个AD7705和一个AD7706时,逻辑输出引脚(DRDY)不能共用一个,当新的数据准备好了,DRDY置低电平,告诉主机你来读取数据吧(踩坑1)
如果有两片以上级联CS、DRDY引脚不能共用;SCK、MISO、MOSI引脚可以共用
1.2 功能方框图

从图中可以看出,只有一个PGA且增益的范围为1~128。AD7705、AD7706都是双通道,在不同读取不同通道时,如果要设置不同增益,需要在每次读取之前设置寄存器。(踩坑:一开始把每个通道初始化,虽然能够读取数据,但是增益不对)
1.3 硬件电路设计



二、程序
2.1 配置DRDY引脚
使用中断的方式:
/**
* @brief 配置 7705--PB0,7706--PB1 为线中断口,并设置中断优先级
* @param 无
* @retval 无
*/
void EXTI_AD7705_7706_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/*开启AD7705中断GPIO口的时钟*/
__HAL_RCC_GPIOB_CLK_ENABLE();
/* 选择 EXTI 中断源 */
GPIO_InitStruct.Pin = AD7705_DRDY_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(AD7705_DRDY_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = AD7706_DRDY_GPIO_PIN;
HAL_GPIO_Init(AD7706_DRDY_GPIO_PORT, &GPIO_InitStruct);
/* EXTI0 interrupt init*/
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
/* EXTI1 interrupt init*/
HAL_NVIC_SetPriority(EXTI1_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
}
2.2 SPI引脚、初始化
这里代码模块更好:
/**
* @brief SPI_AD7705初始化
* @param 无
* @retval 无
*/
void AD7705_7706_SPI_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 使能 AD7705_SPI 及GPIO 时钟 */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*!< AD7705_SPI 时钟使能 */
__HAL_RCC_SPI1_CLK_ENABLE();
/*!< 配置 AD7705_SPI 引脚: SCK */
GPIO_InitStruct.Pin = SPI_SCK_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(SPI_SCK_GPIO_PORT, &GPIO_InitStruct);
/*!< 配置 AD7705_SPI 引脚: MOSI */
GPIO_InitStruct.Pin = SPI_MOSI_GPIO_PIN;
HAL_GPIO_Init(SPI_MOSI_GPIO_PORT, &GPIO_InitStruct);
/*!< 配置 AD7705_SPI 引脚: MISO */
GPIO_InitStruct.Pin = SPI_MISO_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;
HAL_GPIO_Init(SPI_MISO_GPIO_PORT, &GPIO_InitStruct);
/*!< 配置 AD7705_片选 引脚: CS */
GPIO_InitStruct.Pin = AD7705_CS_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(AD7705_CS_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = AD7706_CS_GPIO_PIN;
HAL_GPIO_Init(AD7706_CS_GPIO_PORT, &GPIO_InitStruct);
/* 停止信号 AD7705: CS1 CS2 @引脚高电平*/
AD7705_CS_High();
AD7706_CS_High();
/* AD7705_SPI 模式配置 */
// AD7705芯片 支持SPI模式0及模式3,据此设置CPOL CPHA
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;//设置SPI工作模式:设置为主SPI
hspi1.Init.Direction = SPI_DIRECTION_2LINES;//设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;//设置SPI的数据大小:SPI发送接收8位帧结构
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;//选择了串行时钟的稳态:时钟悬空高
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;//数据捕获于第二个时钟沿
hspi1.Init.NSS = SPI_NSS_SOFT;//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;//定义波特率预分频的值:波特率预分频值为64
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;//CRC值计算的多项式
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
/* 使能 FLASH_SPI */
__HAL_SPI_ENABLE(&hspi1);
}
2.3 读写一个字节
/**
* @brief 读写一个字节
* @param CS,TxData
* @retval 读取的8位数据
*/
void SPIx_ReadWriteByte(uint8_t* TxData, uint8_t* RxData,uint8_t num)
{
while (__HAL_SPI_GET_FLAG(&hspi1, SPI_FLAG_TXE) == RESET); //检查指定的SPI标志位设置与否:发送缓存空标志位
HAL_SPI_TransmitReceive(&hspi1,TxData,RxData,num,1000);
}
2.4 读写16位的数据
/**
* @brief AD7705读16位数据
* @param 无
* @retval 读取的16位数据
*/
uint16_t ReadAD7705_7706_16BitValue(void)
{
uint16_t Ret = 0;
spi_tx_data[0] = 0xFF;
spi_tx_data[1] = 0xFF;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,2);
Ret = spi_rx_data[0] << 8;
Ret |= spi_rx_data[1];
return(Ret);
}
2.5 写通道数据
/**
* @brief 写AD7705 Channel数据
* @param Channel
* @retval
*/
uint16_t ReadAD7705(uint8_t Channel)
{
uint16_t Ret = 0;
AD7705_CS_Low();
AD7705_ch_Init(Channel);
spi_tx_data[0] = 0x38 + Channel;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //设置读当前通道数据
Ret = ReadAD7705_7706_16BitValue();
AD7705_CS_High();
return Ret;
}
/**
* @brief 写AD7706 Channel数据
* @param Channel
* @retval
*/
uint16_t ReadAD7706(uint8_t Channel)
{
uint16_t Ret = 0;
AD7706_CS_Low();
AD7706_ch_Init(Channel);
spi_tx_data[0] = 0x38 + Channel;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //设置读当前通道数据
Ret = ReadAD7705_7706_16BitValue();
AD7706_CS_High();
return Ret;
}
2.6 写片内寄存器数据
**
* @brief AD7705设置有效通道的增益
* @param Channel通道
* @retval 无
*/
void AD7705_ch_Init(uint8_t Channel)
{
uint8_t i;
for(i = 0; i < 5; i++)
{
spi_tx_data[0] = 0xFF;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //持续DIN高电平写操作,恢复AD7705接口
}
switch(Channel)
{
case 0 :
spi_tx_data[0] = 0x20;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //通道1 ,下一个写时钟寄存器
spi_tx_data[0] = 0x04;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //写时钟寄存器设置更新速率为50Hz
spi_tx_data[0] = 0x10;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //通道1 ,下一个写设置寄存器
spi_tx_data[0] = 0x76;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //写设置寄存器 ,设置成单极性、缓冲、增益为8、滤波器工作、自校准 /**/
break;
case 1 :
spi_tx_data[0] = 0x21;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //通道2 ,下一个写时钟寄存器
spi_tx_data[0] = 0x04;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //写时钟寄存器设置更新速率为50Hz
spi_tx_data[0] = 0x11;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //通道2 ,下一个写设置寄存器
spi_tx_data[0] = 0x46;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //写设置寄存器 ,设置成单极性、缓冲、增益为8、滤波器工作、自校准
break;
}
}
/**
* @brief AD7706有效通道的增益设置
* @param CS
* @retval 无
*/
void AD7706_ch_Init(uint8_t Channel)
{
uint8_t i;
for(i = 0; i < 5; i++)
{
spi_tx_data[0] = 0xFF;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //持续DIN高电平写操作,恢复AD7705接口
}
switch(Channel)
{
case 0:
spi_tx_data[0] = 0x20;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //通道1 ,下一个写时钟寄存器
spi_tx_data[0] = 0x04;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //写时钟寄存器设置更新速率为50Hz
spi_tx_data[0] = 0x10;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //通道1 ,下一个写设置寄存器
spi_tx_data[0] = 0x46;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //写设置寄存器 ,设置成单极性、缓冲、增益为1、滤波器工作、自校准
break;
case 1:
spi_tx_data[0] = 0x21;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //通道2 ,下一个写时钟寄存器
spi_tx_data[0] = 0x04;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //写时钟寄存器设置更新速率为50Hz
spi_tx_data[0] = 0x11;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //通道2 ,下一个写设置寄存器
spi_tx_data[0] = 0x46;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //写设置寄存器 ,设置成单极性、缓冲、增益为1、滤波器工作、自校准
break;
case 3:
spi_tx_data[0] = 0x23;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //通道4 ,下一个写时钟寄存器
spi_tx_data[0] = 0x04;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //写时钟寄存器设置更新速率为50Hz
spi_tx_data[0] = 0x13;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //通道4 ,下一个写设置寄存器
spi_tx_data[0] = 0x46;
SPIx_ReadWriteByte(spi_tx_data,spi_rx_data,1); //写设置寄存器 ,设置成单极性、缓冲、增益为1、滤波器工作、自校准
break;
2.7 中断方式读取
/**
* @brief This function handles EXTI0 interrupts.
*/
void EXTI0_IRQHandler(void)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
switch(AD7705_7706_Data.Channel7705_Addr)
{
case 0:
AD7705_7706_Data.Pump_Temp = ReadAD7705(1); //读取为上一个通道
break;
case 1:
AD7705_7706_Data.Pump_Voltage = ReadAD7705(0);
break;
}
if(AD7705_7706_Data.Channel7705_Addr++ >= 1)
{
AD7705_7706_Data.Channel7705_Addr = 0;
System_Data.AD7705_Flag = DEF_TRUE;
}
}
/**
* @brief This function handles EXTI1 interrupts.
*/
void EXTI1_IRQHandler(void)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_1);
switch(AD7705_7706_Data.Channel7706_Addr) //读取为上一个通道
{
case 0:
AD7705_7706_Data.Pump_Current = ReadAD7706(1);
break;
case 1:
AD7705_7706_Data.Sensor_Temp = ReadAD7706(3);
break;
case 2:
AD7705_7706_Data.Sensor_AD = ReadAD7706(0);
break;
}
if(AD7705_7706_Data.Channel7706_Addr++ >= 2)
{
AD7705_7706_Data.Channel7706_Addr = 0;
System_Data.AD7706_Flag = DEF_TRUE;
}
}
AD7705、AD7706当前读取的数据为上一个通道的数据,所以当在赋值每个通道数据时依次顺位。举例:比如 AD7705_7706_Data.Pump_Temp 硬件连接的通道是通道0,AD7705_7706_Data.Pump_Voltage 硬件连接的通道是通道1,所以在读取数据时,
AD7705_7706_Data.Pump_Temp = ReadAD7705(1);
AD7705_7706_Data.Pump_Voltage = ReadAD7705(0);
总结
这是我首次撰写的学习与踩坑笔记,记录的是我第一个项目的点滴历程,还有开发一个上位机(使用C#)。我广泛参考了众多前辈的经验分享和技术文档和手册,并亲自动手实践了一番。这份笔记旨在总结关键知识点和个人感悟,便于日后回顾时能够快速找回状态、避免重蹈覆辙。希望它能成为我个人成长道路上的一块垫脚石。
5450

被折叠的 条评论
为什么被折叠?



