我正在尝试通过 SPI 在两个 NucleoF303K8 板之间进行通信。 其中一块板为主控,由 UART 控制。它可以通过 SPI 发送 3 种消息:
{0x01 0x00 0x00 0x00}
表示“读取值”
{0x81 0x00 0x00 0x00}
表示“设置值 = 0”
{0x81 0x01 0x00 0x00}
表示“设置值 = 1”
另一块板是从板。它根据命令设置(并获取)LED 引脚值。
Slave算法如下:
当
NSS_Pin
变低时,EXTI_Callback
被调用,并开始接收1字节
收到第一个字节后,我猜测它是读出命令还是写入命令
如果是读回,我使用
SPI_DMA
来传输值,如果是写入,我使用SPI DMA
来接收值
当
NSS_Pin
再次达到高电平时,如果是writeIn
,则从机处理消息,或者如果是readout
,则等待另一个命令
这是代码:
extern DMA_HandleTypeDef hdma_spi1_tx;
extern DMA_HandleTypeDef hdma_spi1_rx;
extern SPI_HandleTypeDef hspi1;
uint8_t txb[16] = {0x01,0xAA,0xBB};
uint8_t rxb[16] = {};
uint8_t code = 0;
uint8_t op = 0; // 0: idle, 1: readout, 2: writein
void readOut(){
op = 1;
HAL_SPI_Transmit_DMA(&hspi1, txb,3);
}
void receiveWriteIn(){
op = 2;
HAL_SPI_Receive_DMA(&hspi1, rxb, 3);
}
void processWriteIn(){
HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, (GPIO_PinState)rxb[0]);
}
void SystemLoad(){
HAL_GPIO_WritePin(LD3_GPIO_Port,LD3_Pin,GPIO_PIN_SET);
while(1){
}
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
HAL_SPI_DMAStop(&hspi1);
if(GPIO_Pin == NSS_Pin){
if(HAL_GPIO_ReadPin(NSS_GPIO_Port, NSS_Pin) == GPIO_PIN_RESET){
op = 0;
HAL_SPI_Receive_DMA(&hspi1,&code,1);
}else{
if(op == 2){
processWriteIn();
}
op = 0;
code = 0;
}
}
}
inline void mainCbk(SPI_HandleTypeDef *hspi){
HAL_SPI_DMAStop(hspi);
if(hspi == &hspi1){
if(op == 0){
if(code & 0x80){
receiveWriteIn();
}else{
readOut();
}
}
}
}
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi){
mainCbk(hspi);
}
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi){
mainCbk(hspi);
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi){
mainCbk(hspi);
}
CubeMX 中的从站设置为:
1 个 GPIO 输出 (LED) 和 1 个 EXTI
SPI1
,具有 DMARX
(优先级高)和 DMATX
(优先级非常高)并且 NVIC
为 DMA 通道和 SPI1 全局中断启用
NVIC优先级,Debug、SysTick等优先级最高(0),EXTI和SPI1全局中断优先级较低(1)
HCLK 为 64 MHz,PCLK1 为 32 MHz,PCLK2 为 64 MHz
主设备的 SPI 速度为 250 KBit/s
由于某种原因,我有两个主要问题:
writeIn
数据以rxbuffer
循环迁移。 “设置值 = 1”中的 0x01
出现在 rxb[0]
中,然后出现在 rxb[1]
、rxb[2]
中,并从每个 SPI 消息开始。
从设备传输的读出数据也表现得很奇怪: 我在主设备上收到的前两条消息(并使用在线逻辑分析仪查看)正确的值:
{0x00 0x01 0xAA 0xBB}
但后来有些事情发生了变化,它变成了
{0xBB 0x01 0xAA 0xBB}
或
{0xBB 0x00 0x01 0xBB}
或
{0xBB 0x81 0xAA 0xBB}
如果我在读出后尝试写入它就会变得像
{0xBB 0xBB 0xBB 0xBB}
虽然
rxb
中的HAL_SPI_Receive_DMA(&hspi1, rxb, 3);
充满了0
我查看了SPI1.DR寄存器,但它是0,所以我不知道该怎么想。我尝试过在消息 TX 和 RX 回调上进行 DMAStop,也尝试过 SPI_Abort。
我以不同的方式混合了优先级:
一切平等
EXTI > SPI > DMARX > DMATX(和 TX > RX)
DMATX > DMARX > EXTI = SPI
DMATX > DMARX > EXTI > SPI
DMATX > DMARX > SPI > EXTI
我也尝试过使用
HAL_SPI_Transmit_IT
,但即使 DMA 完全被禁用,也得到相同的结果。
理论上它必须正确工作,因为 SPI 缓冲区在下一个字节 si 被传输之前被填充,但它似乎表现得很奇怪。
配置我的设备的正确方法是什么?或者也许MCU无法以这种方式控制,因为它必须使用软件核心来处理消息,并且有正确的方法来做到这一点?
你能让它在没有DMA或中断的情况下工作吗?一步一步来吧
SPI Echo 示例(1 字节命令,1 字节数据,SPI_DataSize_8b / 我手动设置从机选择引脚 / 如果需要单独读取和写入数据,并且需要实现设定值回读,可以使用 FromMaster/ToMaster 数组改进此代码):
void SPI_Slave_Process(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, SPI_HandleTypeDef *hspi, uint8_t* FromMaster, uint8_t* ToMaster) {
static uint8_t RxData;
static uint8_t TxData;
while(!(HAL_GPIO_ReadPin(GPIOx, GPIO_Pin))) {
HAL_SPI_Receive(hspi, &RxData, 1, 2);
TxData = RxData;
HAL_SPI_Transmit(hspi, &TxData, 1, 2);
}
}