亲爱的堆栈溢出用户,我已经构建了一个具有主设备和10个从设备的网络的设备。它们都通过4线SPI进行通信。现在我正在为两个主板编写程序,但它们似乎不起作用,我没有得到预期的响应。
我有一个主板,和10个相同的从板。协议很简单-与SPI一样,任何事务都是由主设备发起的,并发送一个命令。然后,所选择的从机接收上述命令,将忙标志引脚设置为高电平,并检查其是否有效。在解析该命令之后,释放忙碌的bin,并且如果该命令有效,则将与接收到的字节相同的字节发送到主设备,否则发送错误标记。在此之后,执行任何必要的数据交换。我已经尝试将IO配置为常规端口,以及它们的替代功能,我还尝试在每次事务后重置SPI周期,但似乎都不起作用。
这是我得到的结果:https://imgur.com/a/MICEx2f通道是从顶部开始的,分别是: MOSI、MISO、CLK和busy标志。无论如何,我都没有从从属那里得到任何响应。命令被正确解释(来自UART的调试数据),但是不会发回任何内容。
以下是从属设备代码的SPI部分:
uint8_t spi_sendrecv(uint8_t byte)
{
// poczekaj az bufor nadawczy bedzie wolny
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI1, byte);
// poczekaj na dane w buforze odbiorczym
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
return SPI_I2S_ReceiveData(SPI1);
}uint8_t SPI_get_cmd_ack(void)
{
uint8_t cmd;
uint8_t valid_flag;
//In cas if the BF pin was left high
BF_OUT_low();
//Let's wait for some data
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
cmd = SPI_I2S_ReceiveData(SPI1);
//cmd = SPI_get_command();
//Check the cmd
BF_OUT_high();
valid_flag = SPI_check_for_valid_cmd(cmd);
//SPI_reset_flush();
BF_OUT_low();
if(valid_flag == CMD_RET_STATUS_VALID)
{
spi_sendrecv(cmd);
return cmd;
}
else
{
spi_sendrecv(CMD_ERROR);
return CMD_ERROR;
}
}这是主要的部分:
//Sends a command to a slave device
//Param1: slave device no, from 0 to 9
//Param2: command to send
//Retval: command send success or failure:
//DATA_TRANSFER_OK or DATA_TRANSFER_ERR
uint8_t SPI_send_command(uint8_t slave_no, uint8_t cmd)
{
uint8_t cnt = 0;
uint8_t rx_cmd;
//SPI_reset();
//Select the correct slave
SPI_select_slave(0);
delay_ms(0);
SPI_select_slave(slave_no);
delay_ms(0);
//Transmit the cmd
SPI_sendrecv(cmd);
//SPI_reset();
//Wait for the busy flag indication
while(SPI_get_busy_flag(slave_no) == Bit_RESET)
{
if(cnt < SPI_RETRY_COUNT)
{
++cnt;
delay_ms(1);
}
else
{
SPI_select_slave(0);
return DATA_TRANSFER_ERR;
}
}
//Same for the busy flag on:
while (SPI_get_busy_flag(slave_no) == Bit_SET)
{
if(cnt < SPI_RETRY_COUNT)
{
++cnt;
delay_ms(1);
}
else
{
SPI_select_slave(0);
return DATA_TRANSFER_ERR;
}
}
rx_cmd = SPI_sendrecv(0);
//SPI_reset();
if(rx_cmd == cmd) return DATA_TRANSFER_OK;
else return DATA_TRANSFER_ERR;
}下面是代码的初始化部分,分别是slave和master:
void SPI_init(void)
{
GPIO_InitTypeDef SPI_GPIO;
SPI_InitTypeDef SPI;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | RCC_AHBPeriph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
//GPIOA5 SCK
//GPIOA6 MISO
//GPIOA7 MOSI
SPI_GPIO.GPIO_Mode = GPIO_Mode_AF;
SPI_GPIO.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
SPI_GPIO.GPIO_PuPd = GPIO_PuPd_DOWN;
SPI_GPIO.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &SPI_GPIO);
SPI_GPIO.GPIO_Pin = GPIO_Pin_15;
SPI_GPIO.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &SPI_GPIO);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_SPI1);
//Busy flag
SPI_GPIO.GPIO_Mode = GPIO_Mode_OUT;
SPI_GPIO.GPIO_OType = GPIO_OType_PP;
SPI_GPIO.GPIO_Pin = GPIO_Pin_5;
GPIO_Init(GPIOC, &SPI_GPIO);
/*SPI_GPIO.GPIO_Mode = GPIO_Mode_IN;
SPI_GPIO.GPIO_PuPd = GPIO_PuPd_UP;
SPI_GPIO.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOA, &SPI_GPIO);*/
SPI.SPI_CPHA = SPI_CPHA_1Edge;
SPI.SPI_CPOL = SPI_CPOL_Low;
SPI.SPI_DataSize = SPI_DataSize_8b;
SPI.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI.SPI_FirstBit = SPI_FirstBit_MSB;
SPI.SPI_Mode = SPI_Mode_Slave;
SPI.SPI_NSS = SPI_NSS_Hard;
SPI_Init(SPI1, &SPI);
SPI_Cmd(SPI1, ENABLE);
SPI_aux_tim_conf();
}static void SPI_IO_conf(void)
{
//Struct
GPIO_InitTypeDef SPI_IO;
//CLK
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOE, ENABLE);
//Conf
SPI_IO.GPIO_Mode = GPIO_Mode_AF;
//5 - SCK, 6 - MISO, 7- MOSI
SPI_IO.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7 | GPIO_Pin_6;
SPI_IO.GPIO_PuPd = GPIO_PuPd_DOWN;
SPI_IO.GPIO_OType = GPIO_OType_PP;
SPI_IO.GPIO_Speed = GPIO_Speed_25MHz;
//Init
GPIO_Init(GPIOA, &SPI_IO);
//Connect to SPI periph
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
//For busy flag checking
SPI_IO.GPIO_Mode = GPIO_Mode_IN;
SPI_IO.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 |GPIO_Pin_12 |GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
SPI_IO.GPIO_PuPd = GPIO_PuPd_DOWN;
SPI_IO.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOE, &SPI_IO);
SPI_IO.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOB, &SPI_IO);
}
static void SPI_periph_conf(void)
{
//Struct
SPI_InitTypeDef SPI_conf;
//CLK
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
//Conf
//SysClk = 84000000
//84/64 = 1,3125MHz
SPI_conf.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
SPI_conf.SPI_CPHA = SPI_CPHA_1Edge;
SPI_conf.SPI_CPOL = SPI_CPOL_Low;
//SPI_conf.SPI_CRCPolynomial =
SPI_conf.SPI_DataSize = SPI_DataSize_8b;
SPI_conf.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_conf.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_conf.SPI_Mode = SPI_Mode_Master;
SPI_conf.SPI_NSS = SPI_NSS_Soft;
//Conf, enable
SPI_Init(SPI1, &SPI_conf);
SPI_Cmd(SPI1, ENABLE);
//SPI_Cmd(SPI1, DISABLE);
}正如您在示波器上看到的,从机没有响应,预期的响应与主机在上一个周期中发送的命令相同。例如,我发送了一个0x01存在命令,从机应该用相同的字节响应,在此之后,应该发生任何其他交换,这些交换还没有实现。
致以最好的问候,马雷克
发布于 2019-10-07 22:09:15
从您的图像看,在发送数据后,CLK似乎保持较低。在SPI中,Master是时钟的唯一调控者。
来自STM32F411xC/E reference manual, p 578
忙标志
此BSY标志由硬件设置和清除(写入此标志无效)。BSY标志指示SPI的通信层的状态。
设置BSY时,表示SPI正忙于通信。在主机模式/双向接收模式(MSTR=1和BDM=1以及BDOE=0)中有一个例外,即在接收期间BSY标志保持低电平。
如果软件要禁用SPI并进入暂停模式(或禁用外设时钟),则BSY标志可用于检测传输结束。这样可以避免损坏最后一次传输。为此,必须严格遵守下面描述的过程。
BSY标志对于避免多主机系统中的写冲突也很有用。
除主机模式/双向接收模式(MSTR=1、BDM=1和BDOE=0)外,BSY标志在传输开始时置1。
已清除此选项:
传输完成时的SPI (在主模式下除外,如果通信是continuous)
当通信不连续时,每次通信之间的BSY标志为低电平。
当通信持续时:
之间的一个SPI时钟周期内,BSY标志变为低。
注意:不要使用BSY标志来处理每个数据传输或接收。最好改用TXE和RXNE标志
所以我认为你在发送数据后在主机中等待忙碌的标志可能会无限期地锁定。试试这个(代码使用普通的CMSIS,但它应该是可以理解的):
GPIOB->BSRR |= GPIO_BSRR_BR6; //slave select
while(! (SPI1->SR & SPI_SR_TXE)); //wait for Tx buffer empty
SPI1->DR = 0x01; //send 0x01
while(! (SPI1->SR & SPI_SR_RXNE)); //wait for Rx buffer not empty (receive 0x0 sent by the slave during our sending 0x01 since it's 4-wire SPI)
uint8_t tmp = SPI1->DR; //we don't need that value, but need to read DR in order to reset RXNE flag
SPI1->DR = 0x0; //we need to trigger send in order to receive
while(! (SPI1->SR & SPI_SR_RXNE)); //wait for Rx buffer not empty (our response)
response = SPI1->DR;
while(SPI1->SR & SPI_SR_BSY); //now we can wait for SPI to end communications
GPIOB->BSRR |= GPIO_BSRR_BS6; //slave deselect发布于 2019-04-10 16:41:14
谢谢你的帮助。经过长时间的工作,我设法让它工作,通过在每次交易后重置Slave设备中的SPI外设:
void SPI_reset_flush(void)
{
//Reset the periph and registers
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, ENABLE);
SPI_aux_tim_wait();
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, DISABLE);
SPI_aux_tim_wait();
SPI_Cmd(SPI1, ENABLE);
}12.04.2019实际上,我认为上面提到的解决方案并不是最好的。问题是,我没有等待SPI缓冲区清空,这导致随机发送数据,并且我失去了设备之间的同步。我后来重写了代码,并坚持在参考手册中的TX/RX程序。
致以最好的问候,马雷克
https://stackoverflow.com/questions/55587289
复制相似问题