STM32,主从设备互不响应

     2023-02-16     216

关键词:

【中文标题】STM32,主从设备互不响应【英文标题】:STM32, master and slave devices not responding to each other 【发布时间】:2019-08-30 10:06:22 【问题描述】:

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

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

这是我得到的: https://imgur.com/a/MICEx2f 频道分别来自顶部: 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;
    

这是 MASTER 部分:

//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 或 CS 信号……如果未选择,从机将不会应答。可以试试 1 slave 1 master 共享 CLK 和 CS 信号吗? 很棒的帖子。您能否断开所有从机,仅将示波器连接到主机 SPI 线和主机的从机选择线并确认主机正确发送数据?这样你就会知道,至少 master 工作正常。 感谢您的回复。当然有Clock和CS信号,后者是看不到的,在示波器上放大后可以看到时钟:!Oscillogram CS是通常的低电平有效信号,我没有费心去描绘它,因为我知道它工作正常。我也尝试过每种设备之一,它似乎工作相同。大师也在发送正确的数据,我已经按照你的建议进行了检查,@KamilCuk。最好的问候 那么图片实际上显示了什么? MOSI实际上不是MOSI吗? MISO 是时钟……数据在哪里? “2”是一些随机噪音? “1”是一些不相关的信号? 【参考方案1】:

感谢您的帮助。经过很长时间后,我设法通过在每次事务后重置从设备中的 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 程序。

最好的问候,马雷克

【讨论】:

【参考方案2】:

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

来自STM32F411xC/E reference manual, p 578:

忙碌标志

此 BSY 标志由硬件设置和清除(写入此标志无效)。 BSY标志表示SPI通信层的状态。

当设置 BSY 时,表示 SPI 正忙于通信。在主模式/双向接收模式(MSTR=1 和 BDM=1 和 BDOE=0)中有一个例外,其中 BSY 标志在接收期间保持低电平。

如果软件想要禁用 SPI 并进入暂停模式(或禁用外设时钟),BSY 标志可用于检测传输结束。这样可以避免破坏上次传输。为此,必须严格遵守下述程序。

BSY 标志对于避免多主机系统中的写入冲突也很有用。

BSY 标志在传输开始时设置,除了主模式/双向接收模式(MSTR=1 和 BDM=1 和 BDOE=0)。

已清除:

传输完成时(如果通信是连续的,则在主模式下除外) 当发生主模式故障时禁用 SPI (MODF=1)

当通信不连续时,每次通信之间BSY标志为低。

当通信持续时:

在主模式下,BSY 标志在所有传输期间保持高电平 在从模式下,BSY 标志在每次传输之间的一个 SPI 时钟周期内变为低电平

注意:不要使用 BSY 标志来处理每个数据传输或接收。最好改用 TXE 和 RXNE 标志

所以我认为您在发送数据后等待 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

【讨论】:

课程实习stm32主从蓝牙计算器+温度测量

说明:对于主从蓝牙计算器项目中的代码都是本人经过思考之后自行创作出来的,没有经过任何的网上抄录,由于课程实习的要求不高,所以我就没有对一些出现的bug进行修改(没有删除功能等)。但是基... 查看详情

stm32所有定时器都可以主从模式吗

参考技术Astm32的高级定时器和普通定时器的四个通道是完全一样的CH1,2,3,4.不同的是它的CH1,2,3各带有一个反向端CH1N,2N,3N,反向端可以通过配置寄存器实现比较常见的功能,比如互补输出,反向输出或有选择的任意端口输出,反向端和... 查看详情

基于stm32f429+hal库编写的定时器主从门控模式级联输出固定个数pwm脉冲的程序(代码片段)

硬件设备  42步进电机,步进电机驱动器,正点原子F429开发板开发软件  keil5,Cube综述  一般要精准的控制电机,就要控制单片机的引脚输出指定个数的PWM波,有多种可实现的方法,其中最好用的方法是用定... 查看详情

stm32怎么添加芯片包无响应

参考技术A1、stm32使用过程中出现芯片超时无应答,无法连接的情况。2、跳线帽是否连接正常。3、stm32是否设置正确。4、如果上述都正常,利用stm32的设定选项字节,选中芯片类型,进行恢复出厂设置即可。 查看详情

是否可以对 STM32 设备进行无线编程?

】是否可以对STM32设备进行无线编程?【英文标题】:IsitpossibleprogramSTM32devicewireless?【发布时间】:2021-06-2721:09:54【问题描述】:我有一个STM32-discovery板,我正在尝试不使用任何电缆对其进行编程。在我实习的地方,他们首先希... 查看详情

stm32高级定时器

Stm32高级定时器(二)1主从模式:主?从?谈论主从,可知至少有两个以上的触发或者驱动信号,stm32内部有多个定时器,可以相互之间驱动或者控制。主模式:定时器使能只受驱动时钟控制或者输出控制信号(TRGO)。从模式:复位模式... 查看详情

stm32的“外部中断”和“事件”怎么理解

...而事件则是对片内其他模块发出脉冲触发信号,具体其他设备怎么响应,就看这个模块自己怎么决定了。参考技术A你可以去百度文库查找一个交外部中断与外部事件的区别的稳定。我是看了就忘记了,因为没有实际用过。外部... 查看详情

如何将 STM32f4 编程为 SPI 从设备

】如何将STM32f4编程为SPI从设备【英文标题】:HowtoprogramSTM32f4asSPISlave【发布时间】:2021-12-0918:00:41【问题描述】:我在SPI从模式下编码STM32F407时遇到问题,在我的例子中,Master是ADE7880IC,Slave是STM32F407,.我是初学者,我已经连接... 查看详情

在 STM32 设备中读取和写入文件到闪存的末尾

】在STM32设备中读取和写入文件到闪存的末尾【英文标题】:ReadingandWritingfiletoendofflashmemoryinSTM32device【发布时间】:2020-01-3005:27:24【问题描述】:我有一些功能正常的固件正在部署到具有64K闪存的STM32部件(从地址0x8000000开始)... 查看详情

使用 STM32 USB 设备库的闪存作为大容量存储设备

】使用STM32USB设备库的闪存作为大容量存储设备【英文标题】:FlashmemoryasmassstoragedeviceusingSTM32USBDeviceLibrary【发布时间】:2017-11-1321:50:23【问题描述】:我的板上有这个闪存IC,它连接到我的STM32F04ARM处理器。处理器的USB端口可供... 查看详情

stm32抢占优先级和响应优先级

一、抢占优先级和响应优先级  STM32的中断向量具有两个属性,一个为抢占属性,另一个为响应属性,其属性编号越小,表明它的优先级别越高。  抢占,是指打断其他中断的属性,即因为具有这个属性会出现嵌套... 查看详情

stm32学习系列之中断优先级

...组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。这里写图片描述抢占优先级&响应优先级1.高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。2.抢占优先级相同的中断,高响应优先级不... 查看详情

STM32F7 发现 - USB FS 主机/设备模式检测

】STM32F7发现-USBFS主机/设备模式检测【英文标题】:STM32F7Discovery-USBFShost/devicemodedetection【发布时间】:2018-04-1517:14:44【问题描述】:STM32F723IEK6Discovery板具有全速USB接口。我试图初始化它是徒劳的。从未从主机接收到复位信号,... 查看详情

stm32合适在工业设备上使用吗

  1.STM32F407ZG应用  电机驱动和应用控制  医疗设备  工业应用:PLC,变频器,断路器  打印机和扫描仪  报警系统,可视对讲,暖通空调  家用音响设备  2.STM32F407ZG概述  STM32F407ZG系列是基于高性能的ARM?Cort... 查看详情

我无法在基于自定义 USB CDC 类的 STM32 设备上接收超过 64 个字节

】我无法在基于自定义USBCDC类的STM32设备上接收超过64个字节【英文标题】:Ican\'treceivemorethan64bytesoncustomUSBCDCclassbasedSTM32device【发布时间】:2021-03-2911:37:04【问题描述】:目前我尝试将720字节从Windows应用程序发送到自定义STM32设... 查看详情

stm32dfu(devicefirewaveupdate)更新设备程序(bootload&iap)参考文献

文档:AN2557STM32F10xin-applicationprogrammingusingtheUSART...介绍了USART_IAP的使用方法;AN2606STM32microcontrollersystemmemorybootmode...STM32全系列的bootloader的介绍;AN3154CANprotocolusedintheSTM32bootloader...拥有CAN口b 查看详情

stm32中的抢占优先级响应优先级概念

STM32(Cortex-M3)中有两个优先级的概念——抢占式优先级和响应优先级,有人把响应优先级称作‘亚优先级‘或‘副优先级‘,每个中断源都需要被指定这两种优先级。具有高抢占式优先级的中断可以在具有低抢占式优先级的中断... 查看详情

stm32中断优先级:响应优先级(子优先级)抢占优先级

查看详情