STM32 SPI NSS信号理解和DMA传输遇到的问题

发布者:DataExplorer最新更新时间:2022-02-25 来源: eefocus关键字:STM32  SPI  DMA传输 手机看文章 扫描二维码
随时随地手机看文章

最近做个项目,用到了SPI,遇到一些问题。   


SPI,四根线,MISO,MOSI,SCK,和NSS,这其中NSS用起来最容易踩坑。NSS是片选线,是用于选择从器件的引脚,可让SPI主器件与从器件进行单独通信,从而避免数据线上的竞争。


问题1:从机发送数据给主机

要知道,SPI主机发数据,从机去收。但是从机发数据,主机可以不理会。因为主机控制着SCK线,从机若想要发送数据,只能去通知主机来“读”。怎么通知?


从机若有数据要发送给主机,可以用一根 INT 线来通知,拉低这根线,主机检测到 INT线被拉低,就可以发送一个废字节,从而去交换从机要发送的有效数据。因为当主机往DR寄存器里面写数据时,就会自动开启SCK,在SCK的作用下,主从机的数据就会被交换。


问题2: 硬件,软件模式的选择

我们在配置SPI的时候,会配置NSS是由软件管理,还是硬件管理。看下图

可以看到,左边NSS是硬件的引脚,是实实在在的引脚,右边的引脚是内部的NSS


NSS相关的控制由CR1寄存器的 SSM、SSI 以及 CR2 寄存器的 SSOE 位来控制。


SSM用来配置是硬件模式还是软件模式。SSI用来确定在软件模式下内NSS输入的极性,SSOE用来决定是否允许内部NSS信号送出的NSS引脚上。


所谓硬件模式(SSM=0,二选一处0端有效),就是内部NSS的信号来自于外部NSS引脚,是确确实实的硬东西(引脚)送过来的。


所谓软件模式(SSM=1,二选一处1端有效),内部NSS信号来自于内部SSI标志位,用户可以利用软件设置SSI,来控制内NSS。


需要注意的是,这里的软件模式可能和你理解的软件模式不一样,这里的软件模式是指通过写SSI 位的方式去input 到内部的NSS。我认为这个软件模式的作用是当 STM32的 SPIx 外设作为从机的时候,通过上述的软件模式可以手动的去控制SPI 是否选通从而与主进行机通信(忽略掉主机给出的片选信号)。至于作为主机端的SPI  软件片选操作,下面有说明。


1:硬件模式

我最先是用硬件的模式去控制,首先,要配置相关的NSS引脚,这个不能像软件模式一样随便用一个引脚。之后按照上面的NSS框图去配置,将内部NSS引到硬件的NSS引脚。我用的是标准库的库函数版本,需要注意的是,标准库中SPI的结构体SPI_InitTypeDef 中,没有SSOE的配置,这个SSOE位是在SPI_CR2寄存器中,所以配置SPI成硬件模式,在标准库下,其他的库我不清楚,一定要加上这句话。

SPI1->CR2|=0X0004;        //打开SSOE位,允许内部NSS信号送出


这样,才能将内部的NSS引到硬件的NSS引脚上。做完这些之后,还会遇到一个问题,那就是在发送数据时,NSS引脚会正常的被拉低,但是无法正常拉高。这个是因为在硬件模式下,NSS只有在SPI失能时才会结束拉低。


所以,在每次发送数据之前要先使能SPI,发送完毕后要失能SPI,才能达到在发送数据期间,NSS线被正常的拉高拉低。


2:软件模式

后来由于某些原因,主机又使用了软件模式,软件模式下,你需要随便找一个引脚进行配置,然后在每次发送之前将其拉低,发送结束后将其拉高。


控制起来和我上面说的硬件模式差不多。但是硬件模式的速度是要快于软件模式的。软件模式的优势在于能够同时控制几个从机,通过不同的NSS来与不同的从机通信。


问题3:SPI收发通过DMA传输,会导致最后一个数据有误。


这个问题当时困扰了好久,后来用逻辑分析仪去抓波形,才发现了原因,因为在发送一帧数据的时候,最后一个字节时,片选出了问题。片选被提前拉高。


因为我是用DMA将数据传输到SPI的DR寄存器,在DMA将数据传输完成后,会去触发DMA传输完成中断,在中断中会去通知任务拉高NSS线(用的FreeRTOS),问题就出在这个地方,因为这个DMA传输完成中断,只是DMA传输完所有的数据,也就是都将数据传给DR寄存器后才会触发。并不是SPI传输结束才会触发。举个例子,我一帧数据15个字节。按理说应该是在15个字节发送完毕后才会拉高NSS线。但是用DMA传输的话,DMA在把最后一个字节丢给DR寄存器后,就会立即触发DMA传输完成中断,从而去拉高NSS线。但是此时DR寄存器中还有一个字节的数据。在从机端,检测到NSS线被拉高,自然会以为已经结束。也就会丢掉最后一个字节。


解决办法很简单,就是在拉高之前,去判断下SPI是否忙,如下。


while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET){}//Wait for SPI not busy


GPIO_SetBits(GPIOA,GPIO_Pin_4);


这样,才是正常的,在数据传输完后拉高片选线。达到结束一帧传输的目的。

关键字:STM32  SPI  DMA传输 引用地址:STM32 SPI NSS信号理解和DMA传输遇到的问题

上一篇:STM32+ESP8266获取网络时间和天气
下一篇:STM32_SDIO

推荐阅读最新更新时间:2024-11-12 10:19

经典_STM32_ADC多通道采样的例子
STM32 ADC多通道转换 描述:用ADC连续采集11路模拟信号,并由DMA传输到内存。ADC配置为扫描并且连续转换模式,ADC的时钟配置为12MHZ。在每次转换结束后,由DMA循环将转换的数据传输到内存中。ADC可以连续采集N次求平均值。最后通过串口传输出最后转换的结果。 程序如下: #i nclude stm32f10x.h //这个头文件包括STM32F10x所有外围寄存器、位、内存映射的定义 #i nclude eval.h //头文件(包括串口、按键、LED的函数声明) #i nclude SysTickDelay.h #i nclude UART_INTERFACE.h #i nclude stdio.h #def
[单片机]
STM32入门——寄存器
GPIO学习: 引脚的分类:1.电源引脚 2.晶振引脚 3.复位引脚 4.下载引脚 5.BOOT引脚 6.GPIO引脚 GPIO的结构:1.保护二极管 --引脚两端连接上两个二极管,设置好高低电压,这样超过这个电压之后二极管导通就会保护里面的电路。这能处于两端电压设置的中间值。2.上下拉电阻--上下拉电阻在输入方向门口,用于稳定电压 3.P-MOS和N-MOS管 --- 这是输出模式的门口,推挽输出和开漏输出,推挽输出3.3V ,靠命令传来的电压,激活开关,使他们链接对应的高低电压传出去。 开漏输出能输出5V电压,开漏输出就是控制开关开或者关,上边要接上一个上拉电阻,关掉就是低电压,开开就是高电压。 4.输出寄存器---输出寄存器
[单片机]
STM32学习日志--使用DMA功能自动更新PWM的输出
/******************************************************************************* 编译环境: EWARM V5.30 硬件环境: DZY2.PCB STM32 FW: V3.0.0 作者 : szlihongtao ****************************************************************************** REV : V1.00 DATE : 2011-04-18 NOTE : ********************************************
[单片机]
STM32通过DMA采集多通道AD
环境: 主机:XP 开发环境:MDK4.23 MCU:STM32F103CBT6 说明: 通过脚PA1,PA2采集AD。每路AD采集10次。 参考链接:http://hi.baidu.com/kangxuebin/item/f4f4370f9d7f3c123a53ee30 源代码: #include ad_driver.h //全局变量 //AD采样存放空间 __IO uint16_t ADCConvertedValue ; //函数 //初始化AD void init_ad(void) { ADC_InitTypeDef ADC_InitStructure; DMA_InitTypeD
[单片机]
STM32的ADC DMA USART综合学习
学习STM32的ADC转换,在开发板上写程序调试。 四个任务: 1.AD以中断方式(单次)采集一路 2.AD以中断方式连续采集四路 3. AD 以DMA方式采集一路,DMA深度为一级 4. AD 以DMA方式采集四路,每路DMA深度为28级,并滤波,说明滤波原理。 总结: 第一个任务 :ADC以中断方式采集一路ADC,通过配置ADC_InitStructure结构体中的ADC_ScanConvMode,它规定模数转换工作在扫描模式(多通道)还是单次模式(单通道), ADC_InitStructure.ADC_ScanConvMode=DISABLE,为单通道单次模式。 ADC_ContinuousConvMode,
[单片机]
STM32定时器更新事件可以暂停否?
有人使用STM32的定时器的输出比较功能,具体就是输出4个通道的PWM信号。不过 他需要不定时地调整4个通道的占空比,即调整他们的CCR值。但现在有个小问题,那就是新的CCR值的获得往往会能跨越多个目前定时器的计数周期,这样的话,即使开启各个通道CCR值的预装功能,似乎也很保证做到一次性修改。因为它希望新的CCR值被同时更新。换句话说,他担心不同通道新的CCR值分散在不同计数周期生效,可能给应用带来些麻烦。 我们知道,STM32定时器的预装寄存器的值到影子寄存器的更新往往离不开更新事件。一般来讲,只要启动了定时器,更新事件会随着计数器的溢出而自然产生。 既然这样,比方若是在下面四个时刻获得了新的CCR值,有没有办法让这几个
[单片机]
<font color='red'>STM32</font>定时器更新事件可以暂停否?
stm32的swd接口的烧写协议是否公开的呢?
需要用一台好的示波器来抓才能有足够的存储深度,保证你能够过滤掉那个该死的50clock。 按照Arm的手册,每次转换发送方都需要一个TNR但是我观察JLINK的波形却没有那个该死的TNR。 手册中说异步SWD需要,同步不需要-或者相反,但是我没有找到关于同步异步的描述。 姑且不管他,反正目前忽略掉TNR就能够读到该死IDR。 另外JLINK的复位时序很奇怪,大致是 70clk High,0xe79e(注意,SWD是LSB First), 70clk High,0xedb6(这里很奇怪,找不到描述), 70clkHigh,16clk Low,0xa5, 注意这里按照协议应该是TNR位-但是没有实际观测到这个位, 0b1
[单片机]
STM32学习笔记(6.2):LCD的显示
7.程序源代码 main.c文件中的代码: #include stm32f10x_lib.h #include stm32f10x_lcd.h extern unsigned char LCD_Image_BIT ; extern unsigned char LCD_Image_HIT ; void RCC_cfg(); void FSMC_cfg(); void LCD_cfg(); void GPIO_cfg(); void LCD_Show(unsigned char * LCD_Image); int main() { RCC_cfg(); GPIO_cfg(); FSMC_cfg(); LCD_cfg();
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved