c - STM32,主从设备互不响应

标签 c embedded stm32 spi

亲爱的堆栈溢出用户, 我已经构建了一个带有主设备和 10 个从设备的网络的设备。它们都通过 4 线 SPI 进行通信。现在我正在为两个板编写程序,但它们似乎没有工作,我没有得到预期的响应。

我有一个主板和 10 个相同的从板。该协议(protocol)很简单——与 SPI 一样,任何事务均由主设备发起,并发送一条命令。然后,选定的从机接收到一个预先设置的命令,将忙标志引脚设置为高电平,并检查它是否有效。解析命令后,busy bin 被释放,如果命令有效,则将与接收到的相同字节发送给主机,否则发送错误标记。之后,执行任何必要的数据交换。我已尝试将 IO 配置为常规 portf 及其替代功能,我还尝试在每次交易后重置 SPI 外围设备,但似乎没有任何效果。

这是我得到的: https://imgur.com/a/MICEx2f channel 从上往下分别是: MOSI、MISO、CLK 和忙标志。无论如何,我没有得到奴隶的回应。该命令被正确解释(来自 UART 的调试数据),但是没有返回任何内容。

这是 SLAVE 设备代码的 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 存在命令,从站应该以相同的字节响应,之后,应该发生任何其他交换,但尚未实现。

最好的问候,马立克

最佳答案

从您的图像看来,发送数据后 CLK 保持低电平。在 SPI 中,Master 是唯一的时钟调速器。

来自 STM32F411xC/E reference manual, p 578 :

BUSY flag

This BSY flag is set and cleared by hardware (writing to this flag has no effect). The BSY flag indicates the state of the communication layer of the SPI.

When BSY is set, it indicates that the SPI is busy communicating. There is one exception in master mode / bidirectional receive mode (MSTR=1 and BDM=1 and BDOE=0) where the BSY flag is kept low during reception.

The BSY flag is useful to detect the end of a transfer if the software wants to disable the SPI and enter Halt mode (or disable the peripheral clock). This avoids corrupting the last transfer. For this, the procedure described below must be strictly respected.

The BSY flag is also useful to avoid write collisions in a multimaster system.

The BSY flag is set when a transfer starts, with the exception of master mode / bidirectional receive mode (MSTR=1 and BDM=1 and BDOE=0).

It is cleared:

  • when a transfer is finished (except in master mode if the communication is continuous)
  • when the SPI is disabledwhen a master mode fault occurs (MODF=1)

When communication is not continuous, the BSY flag is low between each communication.

When communication is continuous:

  • in master mode, the BSY flag is kept high during all the transfers
  • in slave mode, the BSY flag goes low for one SPI clock cycle between each transfer

Note:Do not use the BSY flag to handle each data transmission or reception. It is better to use the TXE and RXNE flags instead

所以我认为你在发送数据后等待 master 中的忙标志可以无限期地锁定。试试这个(代码使用普通的 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

关于c - STM32,主从设备互不响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55587289/

相关文章:

c - "Invalid write of size 1"和指针具有正确的内存地址,但仍然会抛出错误

c - 停止当前从中断处理程序运行的函数

java - Raspberry Pi 的 JVM

keyboard - 为什么 USB 键盘报告格式不按照定义的标准工作?

c - STM32 USB CDC 长包接收

c - 读取字符串的单个字符以及字符串数组指向的字符串本身

c - 我的程序崩溃了,我不明白为什么它甚至没有到达第一个 printf

eclipse - 在地址 "0xXXXXXX"中断,没有可用的调试信息,或在程序代码之外

c++ - 使用C获取文件夹中的文件列表

linux - 即时电子邮件加密/签名