Files
2026-08-25 17:30:40 +08:00

235 lines
7.1 KiB
C

/**
******************************************************************************
* @file spi.c
* @author
* @version
* @date
* @brief SPI通信程序,用于与AFE芯片(SH3673520)通信
******************************************************************************
* @attention
*
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "global.h"
//SH3673517 SPI通信
//PB12 = SPI1_CS (GPIO推挽输出,软件控制片选)
//PB13 = SPI1_SCK (AF1) - 时钟
//PB14 = SPI1_MISO(AF1) - 主机输入从机输出
//PB15 = SPI1_MOSI(AF0) - 主机输出从机输入
#define PIN_SPI_NSS GPIO_Pin_12
#define PIN_SPI_SCK GPIO_Pin_13
#define PIN_SPI_MISO GPIO_Pin_14
#define PIN_SPI_MOSI GPIO_Pin_15
#define SPI_Enable() GPIO_ResetBits(GPIOB, PIN_SPI_NSS) //使能通信
#define SPI_Disable() GPIO_SetBits(GPIOB, PIN_SPI_NSS) //关闭通信
/*
SPI_MOSI PB15 主设备输出,从设备输入
SPI_MISO PB14 主设备输入,从设备输出
SPI_SCK PB13 时钟
SPI_CS PB12 片选信号
*/
void uf_SPI2_Init(void)
{
/*定义SPI参数*/
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
// 使能SPI2和GPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
// PB13 (SCK), PB15 (MOSI)
GPIO_InitStructure.GPIO_Pin = PIN_SPI_SCK | PIN_SPI_MOSI;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// PB14 (MISO)
GPIO_InitStructure.GPIO_Pin = PIN_SPI_MISO;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// PB12 (CS)
GPIO_InitStructure.GPIO_Pin = PIN_SPI_NSS;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, PIN_SPI_NSS); //初始化拉高CS,关闭SPI通信
/*配置SPI模式*/
SPI_I2S_DeInit(SPI2);
// 配置SPI2与AFE芯片的SPI3通信方式:4线通信,全双工,主模式,数据同边传输,MSB在前
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //全双工模式
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主模式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //数据帧大小为 8 位
// 配置SPI3极性参数CPOL和CPHA
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //时钟极性为1,空闲时SCK电平状态为高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //时钟相位为1,在第二个跳变沿开始采样数据(第一个跳变沿,失效)
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //软件控制 NSS 信号(PB12)
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;//波特率预分频系数为 64 //36MHz/64 = 0.5625MHz < 1MHz
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值的生成多项式=x^8+x^2+x+1,省略高位,可为任意值1,只用到低8位,生成二进制编码为0000111=7
SPI_Init(SPI2, &SPI_InitStructure);
// 使能SPI2
SPI_Cmd(SPI2, ENABLE);
}
//通信失败时,复位SPI
void SPI2_Error(void)
{
//写也无法确有效,因为需要时序
//这里重新初始化SPI
uf_SPI2_Init();
}
//AFE只支持单字节写操作
//可写地址 40H~59H
//rtnval 0-true; other-false
uint8_t AFE_WriteOneByte(uint8_t addr, uint8_t *data)
{
uint8_t tx_buffer[5], rx_buffer[5];
uint8_t i;
uint8_t response;
// 构造发送数据帧: [0x01][reg_addr][write_data][CRC][0x00]
tx_buffer[0] = 0x01; // 写命令
tx_buffer[1] = addr; // 寄存器地址
tx_buffer[2] = *data; // 写入数据
tx_buffer[3] = CRC8_Cal(tx_buffer, 3); // CRC8
tx_buffer[4] = 0x00; // 无效数据接收
// 拉低CS片选
SPI_Enable();
for (i = 0; i < 5; i++)
{
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI2, tx_buffer[i]);
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
rx_buffer[i] = SPI_I2S_ReceiveData(SPI2);
}
// 获取返回值
response = rx_buffer[4];
SPI_Disable();
delay_us(5);
return (response == 0xA5) ? 0 : 1;
}
//AFE可1次读取多个字节操作
//可读取地址 40H~99H
//rtnval 0-success, other-fail
uint8_t AFE_ReadMulByte(uint8_t addr, uint8_t lenth, uint8_t *data)
{
uint8_t tx_buffer[4]; // 发送缓冲区
uint8_t rx_buffer[40]; // 接收缓冲区,最大可读取24字节,留有余量
uint8_t crc_calculated, crc_received;
uint8_t i;
// 构造发送数据帧: [0x02][reg_addr][data_length][0x00]
tx_buffer[0] = 0x02; // 读命令
tx_buffer[1] = addr; // 寄存器地址
tx_buffer[2] = lenth; // 数据长度
tx_buffer[3] = 0x00;
// 拉低CS片选
SPI_Enable();
// 第一阶段:发送时钟和命令,发送(命令/地址/长度/0x00) 接收(0xFF/命令/地址/长度)
for( i=0; i<4; i++)
{
while(!SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)); // 等待发送缓冲区空
SPI_I2S_SendData(SPI2, tx_buffer[i]);
while(!SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE)); // 等待接收完成
rx_buffer[i] = SPI_I2S_ReceiveData(SPI2);
}
// 第二阶段:接收有效数据(0x00)发送接收数据(需要提供接收时钟)
for( i=0; i<lenth; i++)
{
while(!SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE));
SPI_I2S_SendData(SPI2, 0x00); // 发送无效数据维持时钟
while(!SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE));
rx_buffer[4+i] = SPI_I2S_ReceiveData(SPI2);
}
// 第三阶段:接收CRC
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI2, 0x00);
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
rx_buffer[4 + lenth] = SPI_I2S_ReceiveData(SPI2);
// 拉高CS片选
SPI_Disable();
delay_us(5);
// 校验应头(发送时同时接收)
if(rx_buffer[0] != 0xFF || // 第一个字节应为0xFF
rx_buffer[1] != 0x02 || // 回显读命令
rx_buffer[2] != addr || // 回显寄存器地址
rx_buffer[3] != lenth) // 回显数据长度
{
return 0; // 头部校验失败
}
// 提取有效数据(跳过前4个应答字节)
for (i = 0; i < lenth; i++)
{
data[i] = rx_buffer[4 + i]; // 跳过0xFF、命令、地址、长度
}
crc_received = rx_buffer[4 + lenth]; // 最后1字节为CRC
// CRC计算范围:0xFF + 命令 + 地址 + 长度 + 数据,直到CRC前
crc_calculated = CRC8_Cal(&rx_buffer[0], 4 + lenth);
return (crc_received == crc_calculated) ? 0 : 1;
}
//软件复位
//rtnval 0-success, other-fail
uint8_t AFE_Reset(void)
{
uint8_t tx_buffer[5], rx_buffer[5];
uint8_t response;
uint8_t i = 0;
// 构造发送数据帧: [0x0B][0xBB][0xCC][CRC][0x00]
tx_buffer[0] = 0x0B; // 复位命令
tx_buffer[1] = 0xBB; // 固定参数1
tx_buffer[2] = 0xCC; // 固定参数2
tx_buffer[3] = CRC8_Cal(tx_buffer, 3); // CRC8
tx_buffer[4] = 0x00; // 无效数据接收
// 拉低CS片选
SPI_Enable();
for ( i = 0; i < 5; i++)
{
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI2, tx_buffer[i]);
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
rx_buffer[i] = SPI_I2S_ReceiveData(SPI2);
}
// 获取返回值
response = rx_buffer[4];
// 拉高CS片选
SPI_Disable();
return (response == 0xA5) ? 0 : 1;
}