STM32关于SPI2因为DMA通道而异常发送CRC问题描述
STM32常见问题解析(论文资料)
STM32常见问题解析1、时钟安全系统(CSS)时钟安全系统被激活后,时钟监控器将实时监控外部高速振荡器;如果HSE时钟发生故障,外部振荡器自动被关闭,产生时钟安全中断,该中断被连接到Cortex‐M3的NMI的中断;同时CSS将内部RC振荡器切换为STM32的系统时钟源(对于STM32F103,时钟失效事件还将被送到高级定时器TIM1的刹车输入端,用以实现电机保护控制)。
操作流程:1)、启动时钟安全系统CSS: RCC_ClockSecuritySystemCmd(ENABLE); (NMI中断是不可屏蔽的!)2)外部振荡器失效时,产生NMI中断,对应的中断程序:void NMIException(void){if (RCC_GetITStatus(RCC_IT_CSS) != RESET){ // HSE、PLL已被禁止(但是PLL设置未变)…… // 客户添加相应的系统保护代码处// 下面为HSE恢复后的预设置代码RCC_HSEConfig(RCC_HSE_ON); // 使能HSERCC_ITConfig(RCC_IT_HSERDY, ENABLE); // 使能HSE就绪中断RCC_ITConfig(RCC_IT_PLLRDY, ENABLE); // 使能PLL就绪中断RCC_ClearITPendingBit(RCC_IT_CSS); // 清除时钟安全系统中断的挂起位// 至此,一旦HSE时钟恢复,将发生HSERDY中断,在RCC中断处理程序里, 系统时钟可以设置到以前的状态}}3)、在RCC的中断处理程序中,再对HSE和PLL进行相应的处理。
注意:一旦CSS被激活,当HSE时钟出现故障时将产生CSS中断,同时自动产生 NMI。
NMI 将被不断执行,直到CSS中断挂起位被清除。
因此,在NMI的处理程序中 必须通过设置时钟中断寄存器(RCC_CIR)里的CSSC位来清除CSS中断。
stm32DMA错误无法恢复问题
stm32DMA错误⽆法恢复问题stm32f446,程序中需要ADC1、ADC3和DAC同时⼯作。
⾸先,在此⼯程的STM32G474版中,使⽤TIM8同时触发2个AD、⼀个DA⼯作,所以在STM32F446版中也使⽤此策略,结果⽆法实现。
所以使⽤了TIM8-trog触发ADC1,TIM8-ch1触发ADC3,TIM6触发DAC输出波形。
在长时间运⾏后,或者反复进出jlink调试,会导致ADC的DMA过程受阻,ADC的OVERRUN位置位,⼀般清除此位后即可恢复运⾏后来DMA数据进⼀步增加,即使清除OVERRUN位,也不会恢复⼯作了。
由于此时设备已经发到客户⼿中,即使能够复现,也⽆法调试。
所以费了很⼤的劲,通过反复进出调试器的⽅式复现了错误,然后在调试器中对ADC、DMA等寄存器进⾏操作。
发现此时连续扫描型的ADC并未受影响,仍然通过DMA传出数据。
只是定时器触发的ADC阻塞,并且,DMA寄存器不受控了,写⼊值也不发⽣变化。
通过复位DMA时钟的⽅式,才改变了DMA寄存器的值。
然后给DMA重新初始化,程序⼜开始⼯作了:1 RCC->AHB1RSTR |= RCC_AHB1Periph_DMA2;2 RCC->AHB1RSTR &= ~RCC_AHB1Periph_DMA2;3 RCC->AHB1ENR &= ~RCC_AHB1Periph_DMA2;4 RCC->AHB1RSTR |= RCC_AHB1Periph_DMA2;5 RCC->AHB1RSTR &= ~RCC_AHB1Periph_DMA2;也不知道这个RSTR是在ENR之前还是之后,所以反复写了两遍但是这个AD⽤的DMA2,在ADC2上也使⽤了,所以ADC2也需要重新初始化。
结果ADC2通过重启时钟的⽅式,寄存器不会归零,必须⼈⼯归零后才能使⽤。
STM32定时器触发DMA数据传输失败的原因如何解决
相关用户实现代码如下:
从配置过程和代码实现来看,似乎都没有问题。那DMA怎幺就是不动作
呢?
问题出在我们使用上面的函数做DMA传输所关联源端和目标端时,出现
了想当然的情况。
我们利用TIMER事件来作为DMA请求源时,而作为数据传输的源端或
STM32定时器触发DMA数据传输失败的原因如何
解决
有人使用STM32的定时器事件触发DMA,让其将内存数据传输到通信
外设的数据寄存器进行发送,发现DMA根本就不动作。
比方以基于STM32F411的芯片为例,通过TIM3更新事件触发DMA请
求,DMA从内存将数据送到SPI1的数据寄存器,从而完成数据发送。
目的端,都是我们用户指定的。这时就一定要注意源端和目标端是当前DMA
流所支持的。否则就会出现乱点鸳鸯谱,DMA根本可能就跑不起来。就像你
叫了某快递公司,它的服务范围是相对固定的,并非你想去哪里她就能服务
到哪里。
比方上面的例子,如果改成SPI2就没问题。为什幺刚才SPI1不行呢,因
为DMA1根本访问不到SPI1,我们不妨看看基于STM32F411芯片的内部功
情形。换言之,我们在做DMA传输时,为了实现DMA的有效传输,往往
需要结合源端和目的端来调整或选择合适的DMA数据流。结合前面的比
方,如果这家快递公司到不了,我们换一家可能就轻松解决了。
能及总线框图就很清晰了。
从上图不难看出DMA1是访问不到SPI1或SPI4这些外设的,或者说
DMA1就没法访问挂在APB2总线上的外设,只能访问APB1总线上ห้องสมุดไป่ตู้外
设。
小结:一般来讲,基于某触发事件,对应的DMA数据流就可以确定下来
STM32_参考手册-中文
23 串行外设接口(SPI)小容量产品是指闪存存储器容量在16K 至32K 字节之间的STM32F101xx、STM32F102xx和STM32F103xx微控制器。
中容量产品是指闪存存储器容量在64K至128K字节之间的STM32F101xx、STM32F102xx 和STM32F103xx微控制器。
大容量产品是指闪存存储器容量在256K至512K字节之间的STM32F101xx和STM32F103xx微控制器。
互联型产品是指STM32F105xx和STM32F107xx微控制器。
除非特别说明,本章描述的模块适用于整个STM32F10xxx微控制器系列。
23.1 SPI简介在大容量产品和互联型产品上,SPI接口可以配置为支持SPI协议或者支持I2S音频协议。
SPI接口默认工作在SPI方式,可以通过软件把功能从SPI模式切换到I2S模式。
在小容量和中容量产品上,不支持I2S音频协议。
串行外设接口(SPI)允许芯片与外部设备以半/全双工、同步、串行方式通信。
此接口可以被配置成主模式,并为外部从设备提供通信时钟(SCK)。
接口还能以多主配置方式工作。
它可用于多种用途,包括使用一条双向数据线的双线单工同步传输,还可使用CRC校验的可靠通信。
I2S也是一种3引脚的同步串行接口通讯协议。
它支持四种音频标准,包括飞利浦I2S标准,MSB 和LSB对齐标准,以及PCM标准。
它在半双工通讯中,可以工作在主和从2种模式下。
当它作为主设备时,通过接口向外部的从设备提供时钟信号。
警告:由于SPI3/I2S3 的部分引脚与JTAG 引脚共享(SPI3_NSS/I2S3_WS 与JTDI ,SPI3_SCK/I2S3_CK与JTDO),因此这些引脚不受IO控制器控制,他们(在每次复位后) 被默认保留为JTAG用途。
如果用户想把引脚配置给SPI3/I2S3,必须(在调试时)关闭JTAG并切换至SWD接口,或者(在标准应用时)同时关闭JTAG和SWD接口。
stm32串口奇校验原理及程序
stm32串口奇校验原理及程序(最新版)目录一、STM32 串口简介二、奇校验原理三、STM32 串口奇校验程序配置四、奇校验在 STM32 串口通信中的应用实例五、总结正文一、STM32 串口简介STM32 是一款由 STMicroelectronics 公司推出的高性能、低功耗的微控制器。
它具有丰富的外设接口,其中包括串口(UART)。
串口是一种通用的、全双工的通信接口,可以用于设备之间的数据传输。
二、奇校验原理奇校验是一种数据传输时的错误检测方法。
在串口通信中,发送方将数据字符与校验位一起发送给接收方。
接收方收到数据后,通过对数据字符和校验位进行奇偶校验,以判断数据是否传输正确。
如果发现错误,接收方会通知发送方进行重发。
奇校验的原理是:在发送数据时,将数据位按位异或操作,得到一个校验位。
接收方收到数据后,将数据位进行异或操作,如果结果为 0,则表示数据传输正确;如果结果不为 0,则表示数据传输错误。
三、STM32 串口奇校验程序配置在 STM32 中,可以通过以下步骤配置串口奇校验:1.配置 USART:使用 USART_Init() 函数初始化串口,设置波特率、停止位、数据位、校验位等参数。
2.配置奇校验:在 USART_Init() 函数中,将 USART_CHECK_MODE 设置为 USART_CHECK_MODE_奇校验。
3.开启串口:使用 USART_Cmd() 函数开启串口。
4.发送数据:使用 USART_Send() 函数发送数据。
5.接收数据:使用 USART_Receive() 函数接收数据。
四、奇校验在 STM32 串口通信中的应用实例假设我们有一个工业传感采集器,需要通过串口与外部设备进行通信。
通信协议规定,数据位为 8 位,奇校验,停止位为 1。
我们可以按照以下步骤进行 STM32 串口奇校验程序配置:1.初始化串口:使用 USART_Init() 函数,设置波特率为 115200,停止位为 1,数据位为 8,校验位为奇校验。
SPI1SPI2_DMA通信实验(STM32)
SPI1SPI2_DMA通信实验(STM32)STM32学习笔记(⼆)——之SPI_DMA寄存器级操作⼀、实验⽬标学会配置STM32的SPI寄存器和DMA寄存器,实现STM32的SPI1与SPI2通信功能,每次发送⼀字节数据,并可多次发送,如果接收的数据正确,则点亮LED灯。
⼆、实验⽬的加⼊DMA的SPI通信相对于普通SPI通信有什么好处?ST给SPI加了DMA功能出于什么⽬的?我觉得这是很重要的⼀个问题,⼀直边学习边想。
以下是我的看法:减少CPU负荷?我想这应该是DMA最主要的功能,可是对于SPI通信来说,其实⼤部分时候我们需要根据发送的指令->⽬标器件的应答来决定下⼀个指令,所以此时CPU还是需要⼀直等待每次通信的结束。
⽽且像SD卡的操作,是⼀个顺序流的指令操作过程,⽤中断也不容易控制。
那到底加⼊了DMA有什么好处?仔细查看了STM32F10xxx的⽤户⼿册,发现这么⼀⾏字“连续和⾮连续传输:当在主模式下发送数据时,如果软件⾜够快,能够在检测到每次TXE的上升沿(或TXE中断),并⽴即在正在进⾏的传输结束之前写⼊SPI_DR寄存器,则能够实现连续的通信;此时,在每个数据项的传输之间的SPI时钟保持连续,同时BSY位不会被清除。
如果软件不够快,则会导致不连续的通信;这时,在每个数据传输之间会被清除”以及也就是说如果连续传输⽽不使⽤DMA的话,需要CPU不停检测TXE并很快地置⼊SPI->DR的值,对于复杂程序的话这是很难达到的,⽽如果使⽤DMA,就可以轻易实现连续传输,CPU只需等待其完成就好。
我想到的⼀个应⽤就是在写SD卡的时候,每次写⼀个块512字节,就可以⽤到,能提⾼SD卡的写⼊数据速率。
其次还可以降低功耗,记得数字集成电路⽼师说过⼀句话“软件上降低数字电路功耗的⼀个⽅法就是减少电平转换。
”那么连续通信的时候,像SPI的BSY电平转换会⼤⼤减少!最后⼀点,虽然效果不⼤,就是如果不是⽤DMA,那么CPU的⼯作就是搬运⼯,把SPI->DR 的内容搬到内存存储起来,⽽如果使⽤DMA,就省略了这个环节!我想,为什么实现同⼀个功能,有的执⾏起来很流畅,有的却很卡,应该和这些⼩细节的减载有关吧。
STM32的SPI通信总结(含DMA)
STM32--—SPI(DMA)通信的总结(库函数操作)本文主要由7项内容介绍SPI并会在最后附上测试源码供参考:1.SPI的通信协议2.SPI通信初始化(以STM32为从机,LPC1114为主机介绍)3.SPI的读写函数4.SPI的中断配置5.SPI的SMA操作6.测试源码7.易出现的问题及原因和解决方法一、SPI的通信协议SPI(Serial Peripheral Interface)是一种串行同步通讯协议,由一个主设备和一个或多个从设备组成,主设备启动一个与从设备的同步通讯,从而完成数据的交换。
SPI 接口一般由4根线组成,CS片选信号(有的单片机上也称为NSS),SCLK时钟信号线,MISO数据线(主机输入从机输出),MOSI数据线(主机输出从机输入),CS 决定了唯一的与主设备通信的从设备,如没有CS 信号,则只能存在一个从设备,主设备通过产生移位时钟信号来发起通讯。
通讯时主机的数据由MISO输入,由MOSI 输出,输入的数据在时钟的上升或下降沿被采样,输出数据在紧接着的下降或上升沿被发出(具体由SPI的时钟相位和极性的设置而决定).二、以STM32为例介绍SPI通信1.STM32f103 带有3个SPI模块其特性如下:2SPI 初始化初始化SPI 主要是对SPI要使用到的引脚以及SPI通信协议中时钟相位和极性进行设置,其实STM32的工程师已经帮我们做好了这些工作,调用库函数,根据自己的需要来修改其中的参量来完成自己的配置即可,主要的配置是如下几项:引脚的配置SPI1的SCLK,MISO ,MOSI分别是PA5,PA6,PA7引脚,这几个引脚的模式都配置成GPIO_Mode_AF_PP 复用推挽输出(关于GPIO的8种工作模式如不清楚请自己百度,在此不解释),如果是单主单从,CS引脚可以不配置,都设置成软件模式即可.通信参数的设置1.SPI_Direction_2Lines_FullDuplex把SPI设置成全双工通信;2.在SPI_Mode 里设置你的模式(主机或者从机),3.SPI_DataSize是来设置数据传输的帧格式的SPI_DataSize_8b是指8位数据帧格式,也可以设置为SPI_DataSize_16b,即16位帧格式4.SPI_CPOL和SPI_CPHA是两个很重要的参数,是设置SPI通信时钟的极性和相位的,一共有四种模式在库函数中CPOL有两个值SPI_CPOL_High(=1)和SPI_CPOL_Low (=0)。
STM32的SPI通信总结(含DMA)
STM32---SPI(DMA)通信的总结(库函数操作)本文主要由7项内容介绍SPI并会在最后附上测试源码供参考:1.SPI的通信协议2.SPI通信初始化(以STM32为从机,LPC1114为主机介绍)3.SPI的读写函数4.SPI的中断配置5.SPI的SMA操作6.测试源码7.易出现的问题及原因和解决方法一、SPI的通信协议SPI(Serial Peripheral Interface)是一种串行同步通讯协议,由一个主设备和一个或多个从设备组成,主设备启动一个与从设备的同步通讯,从而完成数据的交换。
SPI 接口一般由4根线组成,CS片选信号(有的单片机上也称为NSS),SCLK时钟信号线,MISO数据线(主机输入从机输出),MOSI数据线(主机输出从机输入),CS 决定了唯一的与主设备通信的从设备,如没有CS 信号,则只能存在一个从设备,主设备通过产生移位时钟信号来发起通讯。
通讯时主机的数据由MISO输入,由MOSI 输出,输入的数据在时钟的上升或下降沿被采样,输出数据在紧接着的下降或上升沿被发出(具体由SPI的时钟相位和极性的设置而决定)。
二、以STM32为例介绍SPI通信1.STM32f103 带有3个SPI模块其特性如下:2SPI 初始化初始化SPI 主要是对SPI要使用到的引脚以及SPI通信协议中时钟相位和极性进行设置,其实STM32的工程师已经帮我们做好了这些工作,调用库函数,根据自己的需要来修改其中的参量来完成自己的配置即可,主要的配置是如下几项:引脚的配置SPI1的SCLK, MISO ,MOSI分别是PA5,PA6,PA7引脚,这几个引脚的模式都配置成GPIO_Mode_AF_PP 复用推挽输出(关于GPIO的8种工作模式如不清楚请自己百度,在此不解释),如果是单主单从, CS引脚可以不配置,都设置成软件模式即可。
通信参数的设置1.SPI_Direction_2Lines_FullDuplex把SPI设置成全双工通信;2.在SPI_Mode 里设置你的模式(主机或者从机),3.SPI_DataSize是来设置数据传输的帧格式的SPI_DataSize_8b是指8位数据帧格式,也可以设置为SPI_DataSize_16b,即16位帧格式4.SPI_CPOL和SPI_CPHA是两个很重要的参数,是设置SPI通信时钟的极性和相位的,一共有四种模式在库函数中CPOL有两个值SPI_CPOL_High(=1)和SPI_CPOL_Low ( =0). CPHA有两个值SPI_CPHA_1Edge (=0) 和SPI_CPHA_2Edge(=1)CPOL表示时钟在空闲状态的极性是高电平还是低电平,而CPHA则表示数据是在什么时刻被采样的,手册中如下:我的程序中主、从机的这两位设置的相同都是设置成1,即空闲时时钟是高电平,数据在第二个时钟沿被采样,实验显示数据收发都正常。
STM32串口DMA接收数据错位——暴力解决方法
STM32串⼝DMA接收数据错位——暴⼒解决⽅法背景:两⽚STM32通过串⼝通信,为了减⼩CPU负担,采⽤DMA进⾏通信,发送端为STM32F103C8T6,接收端为STM32F407VET6。
在调试的过程中发现,⼀直出现数据错位的问题,接收端尝试了串⼝空闲中断和串⼝DMA传输完成中断,错位问题依旧,其实我之前遇到过这个问题,那次发送端没有使⽤DMA,⽽是直接⽤串⼝发送,接收端采⽤DMA接收完成中断,检测到错位后,延时重置DMA,直到DMA接收同步后,不再重置,此后DMA便会保持同步,不会错位。
但是这次不知道为什么采⽤上次的⽅法没有解决,因此决定直接⽤最简单粗暴的⽅法——查找,但是弊端是会在中断中运⾏⼀段⽐较占空时间的代码。
说明:主要部分在接收中断(本⽂最后的代码段),发送端发送的DMA数据长度为a,接收端DMA配置的BufferSize为2a,这样即使错位,在2a的数据长度中也⼀定会存在⼀段完整的有效数据。
接收中断中,在接收buffer的前半段查找帧头,找到之后,判断帧头+a-1的位置是否是帧尾,如果是,则基本可以认为中间即为有效数据,将该段数据拷贝到⼀个新的数组中,等待解析。
配置部分:发送端 STM32F103C8T6/* uart3 for communicate with the master */void vUart3Config(void){GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;DMA_InitTypeDef DMA_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);/* USART3_RX GPIOB.11 */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOB, &GPIO_InitStructure);/* USART3_TX GPIOB.10 */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(GPIOB, &GPIO_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);USART_ART_BaudRate = 115200;USART_ART_WordLength = USART_WordLength_8b;USART_ART_StopBits = USART_StopBits_1;USART_ART_Parity = USART_Parity_No;USART_ART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_ART_Mode = USART_Mode_Rx | USART_Mode_Tx;USART_Init(USART3, &USART_InitStructure);USART_Cmd(USART3, ENABLE);USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);{ /* send dma */USART_DMACmd(USART3,USART_DMAReq_Tx,ENABLE);DMA_DeInit(DMA1_Channel2);DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(USART3->DR));DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SendToMaster_Buff;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;DMA_InitStructure.DMA_BufferSize = USART3_DMA_send_buffersize;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;DMA_Init(DMA1_Channel2,&DMA_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);DMA_ITConfig(DMA1_Channel2,DMA_IT_TC,ENABLE);DMA_Cmd(DMA1_Channel2,ENABLE);}}配置部分:接收端 STM32F407VET6void vUTConfig(void){USART_InitTypeDef usart;GPIO_InitTypeDef gpio;NVIC_InitTypeDef nvic;DMA_InitTypeDef dma;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);GPIO_PinAFConfig(GPIOA,GPIO_PinSource9 ,GPIO_AF_USART1);GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);/* USART1_RX GPIOA.10 */gpio.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;gpio.GPIO_Mode = GPIO_Mode_AF;gpio.GPIO_OType = GPIO_OType_PP;gpio.GPIO_Speed = GPIO_Speed_100MHz;gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOA,&gpio);ART_BaudRate = 115200;ART_WordLength = USART_WordLength_8b;ART_StopBits = USART_StopBits_1;ART_Parity = USART_Parity_No;ART_Mode = USART_Mode_Rx | USART_Mode_Tx;ART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Init(USART1,&usart);USART_Cmd(USART1,ENABLE);{ /* receive dma */USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);DMA_DeInit(DMA2_Stream2);dma.DMA_Channel= DMA_Channel_4;dma.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);dma.DMA_Memory0BaseAddr = (uint32_t)ReceiveFromUT_Buffer;dma.DMA_DIR = DMA_DIR_PeripheralToMemory;dma.DMA_BufferSize = USART1_UT_DMA_receive_buffersize;dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;dma.DMA_MemoryInc = DMA_MemoryInc_Enable;dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;dma.DMA_Mode = DMA_Mode_Circular;dma.DMA_Priority = DMA_Priority_VeryHigh;dma.DMA_FIFOMode = DMA_FIFOMode_Disable;dma.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;dma.DMA_MemoryBurst = DMA_MemoryBurst_Single;dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;DMA_Init(DMA2_Stream2,&dma);nvic.NVIC_IRQChannel = DMA2_Stream2_IRQn;nvic.NVIC_IRQChannelPreemptionPriority = 1;nvic.NVIC_IRQChannelSubPriority = 1;nvic.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&nvic);DMA_ITConfig(DMA2_Stream2,DMA_IT_TC,ENABLE);DMA_Cmd(DMA2_Stream2,ENABLE);}}中断部分:发送中断/* USART3 DMA send interrupt */void DMA1_Channel2_IRQHandler(void){if(DMA_GetITStatus(DMA1_IT_TC2)){DMA_ClearFlag(DMA1_IT_TC2);DMA_ClearITPendingBit(DMA1_IT_TC2);// DMA_Cmd(DMA1_Channel2,DISABLE);// DMA_SetCurrDataCounter(DMA1_Channel2,USART3_DMA_send_buffersize);// DMA_Cmd(DMA1_Channel2, ENABLE);}}中断部分:接收中断/* USART1 dma receive for ut */void DMA2_Stream2_IRQHandler(void){uint8_t i = 0;if(DMA_GetFlagStatus(DMA2_Stream2,DMA_IT_TCIF2) == SET){/* 在前半部分查找帧头并校验对应位置是否为帧尾 */for(i=0;i<(USART1_UT_DMA_receive_buffersize/2);i++){if((ReceiveFromUT_Buffer[i] == 0x05)&&(ReceiveFromUT_Buffer[i+USART1_UT_DMA_receive_buffersize-1] == 0x06)) {/* 拷贝有效数据段到待解析数组 */memcpy(ReceiveFromUT_Data,&ReceiveFromUT_Buffer[i],USART1_UT_DMA_receive_buffersize/2);/* 数据解析 */UTReceive();}}/* 没有有效数据 */if(i >= USART1_UT_DMA_receive_buffersize/2){/* 重置DMA */DMA_Cmd(DMA2_Stream2,DISABLE);DMA_SetCurrDataCounter(DMA2_Stream2,USART1_UT_DMA_receive_buffersize);DMA_Cmd(DMA2_Stream2,ENABLE);}DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);DMA_ClearITPendingBit(DMA2_Stream2, DMA_IT_TCIF2);}}——cloudos——2020/4/17。
STM32HAL库I2C工作出错返回I2C_BUSY
STM32HAL库I2C⼯作出错返回I2C_BUSY使⽤stm32cubemx⽣成硬件I2C的代码不过⾃动⽣成的代码,调⽤HAL_I2C_XXX的API⼯作不正常,返回错误代码为I2C_BUSY使⽤STM32的I2C接⼝使⽤时需要注意很多细节,不过HAL库中官⽅已经为⽤户根据这些细节做了处理,可以直接使⽤。
不过这个I2C代码并不稳定,有些板⼦可以⽤,另⼀些则出错.出错现象为调⽤HAL_I2C的API时,返回I2C_BUSY查看寄存器 BUSY位被置位解决⽅案将⾃动⽣成的代码中,HAL_RCC_I2C1_CLK_ENABLE()放到GPIO初始化之前就不会出现这个问题,I2C⼯作正常具体依据没有找到,不过在HAL的说明⽂档中有要求这样的初始化顺序,同时其他的模块如SPI或UART,也都是先初始化模块(SPI,UART),再初始化GPIOvoid HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle){GPIO_InitTypeDef GPIO_InitStruct = {0};if(i2cHandle->Instance==I2C1){/* USER CODE BEGIN I2C1_MspInit 0 */__HAL_RCC_I2C1_CLK_ENABLE(); //添加时钟使能命令/* USER CODE END I2C1_MspInit 0 */__HAL_RCC_GPIOB_CLK_ENABLE();/**I2C1 GPIO ConfigurationPB6 ------> I2C1_SCLPB7 ------> I2C1_SDA*/GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);/* I2C1 clock enable */__HAL_RCC_I2C1_CLK_ENABLE();/* I2C1 interrupt Init */HAL_NVIC_SetPriority(I2C1_EV_IRQn, 0, 0);HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);/* USER CODE BEGIN I2C1_MspInit 1 *//* USER CODE END I2C1_MspInit 1 */}}其他解决⽅式即初始化时,重新置位下I2C,再重新初始化,虽然说这种⽅法也可⾏,只是总感觉不太合适。
stm32串口奇校验原理及程序
stm32串口奇校验原理及程序STM32串口奇校验是一种错误检测和纠正方法,用于保证串口数据传输的准确性。
奇校验在数据位之后插入一个校验位,使得数据位与校验位的总和为奇数。
校验位的值取决于数据位中1的数量,如果数据位中1的数量为偶数,则校验位为1,否则为0。
接收方在接收数据后,会重新计算数据位中1的数量,并与接收到的校验位进行比较,如果不一致则判定数据错误。
以下是使用STM32的HAL库实现串口奇校验的示例程序:1. 首先,需要在CubeMX中配置串口接口的相关参数,包括波特率、数据位、停止位等。
在串口的配置选项中,选择奇校验位。
2. 在生成的代码中找到串口初始化函数,一般是`USARTx_Init()`,并在其配置项的`huart.Init.Parity`中选择`UART_PARITY_ODD`表示奇校验。
3. 在接收中断处理函数中,添加校验位的逻辑。
可以通过`__HAL_UART_GET_FLAG()`函数判断接收完成标志位是否置位,如果接收完成,则通过`__HAL_UART_FLUSH_DRREGISTER()`函数读取接收寄存器的值,并计算数据位中1的数量。
```cif (__HAL_UART_GET_FLAG(&huart, UART_FLAG_RXNE)){uint8_t data =__HAL_UART_FLUSH_DRREGISTER(&huart);uint8_t parityBit = 0;for (int i = 0; i < 8; i++) {parityBit += (data >> i) & 0x01;}parityBit &= 0x01;if ((parityBit == 1 && huart.Init.Parity !=UART_PARITY_ODD) || (parityBit == 0 && huart.Init.Parity != UART_PARITY_EVEN)) {// 校验错误的处理逻辑} else {// 校验正确的处理逻辑}}```以上是实现STM32串口奇校验的基本原理和示例程序。
STM32串口DMA方式发送数据
STM32 串口DMA方式接收2011-04-02 18:13 4458人阅读评论(5) 收藏举报 STM32 是一款基于ARM Cortex-M3内核的32位MCU,主频最高可达72M。
最近因为要在车机上集成TPMS功能,便开始着手STM32的开发工作,STM32F10x系列共有5个串口(USART1~USART5),支持DMA方式通信,DMA 方式由于不需要CPU的参与,而是直接由DMA控制器完成串口数据的读写,因而可以很大程度的提高CPU的利用率。
在使用STM32串口之前需要做一系列的初始化工作:1.RCC(复位和时钟控制寄存器)初始化,启用GPIO、DMA、USART时钟。
2.NVIC(嵌套向量中断控制寄存器)初始化,完成各个硬件中断的配置。
ART初始话,配置串口,设置DMA通道等。
4.DMA初始化,完成DMA的配置。
最后是使能USART和DMA。
下面是通过DMA的方式从串口USART1接收数据,STM32工作后串口数据由DMA控制器接收存到指定buffer,读取数据直接从DMA buffer中读取即可。
发送数据采用非DMA方式,首先将待发送的数据存入到发送队列,然后在任务循环中将队列中的数据发送给USART1。
实例代码如下:[cpp]view plaincopyprint?1. //**********************************************************************************************2. // STM32F10x USART Test3. // compiler: Keil UV34. // 2011-03-28 , By friehood5. //**********************************************************************************************6. static int8u rDMABuffer[64]; // DMA buffer7. static int16u rDMARear = sizeof(rDMABuffer);8.9. static int8u USART_RevBuf[64]; // 串口接收buffer10. s tatic int8u USART_SndBuf[64]; // 串口发送buffer11. s tatic int8u Head=0,Tail=0; // 发送buffer的头尾12.13.14. // 串口任务15. v oid Task_USART(void)16. {17. int16u end;18. if (USART1->SR & (USART_FLAG_ORE | USART_FLAG_NE | USART_FLAG_FE))19. {20. USART_ReceiveData(USART1);21. }22.23. // DMA接收24. end = DMA_GetCurrDataCounter(DMA1_Channel5);25. /*if((sizeof(rDMABuffer)-end)>0)26. dbgprt("DMA available datalen=%d/n",sizeof(rDMABuffer)-end); */27. while(rDMARear != end)28. {29. USART_receive(rDMABuffer[sizeof(rDMABuffer)-rDMARear]);30. if (!(--rDMARear))31. {32. rDMARear = sizeof(rDMABuffer);33. }34. }35.36. //发送37. if(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == SET)38. {39. int8u chr;40. // 从发送队列取出一个字符41. if(PopFront(&chr))42. {43. USART_SendData(USART1, chr);44. dbgprt("USART_SendData:0x%02x/n",chr);45. while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)46. {47. }48. }49. }50. }51.52.53. // USART串口初始化54. v oid dev_USART_init(void)55. {56. USART_InitTypeDef USART_InitStructure;57. GPIO_InitTypeDef GPIO_InitStructure;58. DMA_InitTypeDef DMA_InitStructure;59.60. /* DMA1 Channel5 (triggered by USART1 Rx event) Config */ //参见 STM32 datasheet61. DMA_DeInit(DMA1_Channel5);62. DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;63. DMA_InitStructure.DMA_MemoryBaseAddr = (u32)rDMABuffer;64. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;65. DMA_InitStructure.DMA_BufferSize = sizeof(rDMABuffer);66. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;67. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;68. DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;69. DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;70. DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;71. DMA_InitStructure.DMA_Priority = DMA_Priority_Low;72. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;73. DMA_Init(DMA1_Channel5, &DMA_InitStructure);74.75. USART_ART_BaudRate = 9600;76. USART_ART_WordLength = USART_WordLength_8b;77. USART_ART_StopBits = USART_StopBits_1;78. USART_ART_Parity = USART_Parity_No;79. USART_ART_HardwareFlowControl = USART_HardwareFlowControl_None;80. USART_ART_Mode = USART_Mode_Rx | USART_Mode_Tx;81.82. //配置IO: GPIOA9和GPIOA10分别作为串口TX、RX端。
stm32串口奇校验原理及程序
stm32串口奇校验原理及程序一、STM32串口通信基本概念STM32串口通信是指通过串行通信接口进行数据传输的一种通信方式。
在日常应用中,串口通信广泛应用于嵌入式系统、工业自动化等领域。
STM32作为一款高性能、低成本的微控制器,支持多种串口通信模式,包括奇校验、偶校验等。
二、奇校验原理及其应用奇校验是一种通过检测数据传输过程中校验位(数据位+校验位)中“1”的个数来判断数据是否发生错误的校验方法。
在STM32串口通信中,奇校验的应用能有效提高数据传输的可靠性。
奇校验原理:1.发送数据时,将数据位与校验位(一般为0)组合成发送数据。
2.接收端检测接收到的数据中“1”的个数,若为奇数,则表示数据正确;若为偶数,则表示数据错误。
三、奇校验程序设置与解析1.设置奇校验的USART初始化结构体:```cUSART_InitTypeDef USARTInitStructure;ART_BaudRate = 9600; // 波特率ART_WordLength = USART_WordLength8b; // 数据位:8位ART_StopBits = USART_StopBits1; // 停止位:1位ART_Parity = USART_ParityOdd; // 奇校验ART_HardwareFlowControl =USART_HardwareFlowControlNone;ART_Mode = USART_Mode_Rx |USART_Mode_Tx;HAL_USART_Init(USARTx, &USARTInitStructure); // 初始化串口```2.发送数据:```cuint8_t send_data = 0x55;HAL_USART_Transmit(&USARTx, &send_data, 1, 1000); // 发送1字节数据,等待1000us```3.接收数据:```cuint8_t receive_data;HAL_USART_Receive(&USARTx, &receive_data, 1, 1000); // 接收1字节数据,等待1000us```四、常见问题及解决方案1.数据传输错误:检查波特率、数据位、停止位和校验位设置是否正确。
stm32 can发送失败处理逻辑
stm32 can发送失败处理逻辑下载提示:该文档是本店铺精心编制而成的,希望大家下载后,能够帮助大家解决实际问题。
文档下载后可定制修改,请根据实际需要进行调整和使用,谢谢!本店铺为大家提供各种类型的实用资料,如教育随笔、日记赏析、句子摘抄、古诗大全、经典美文、话题作文、工作总结、词语解析、文案摘录、其他资料等等,想了解不同资料格式和写法,敬请关注!Download tips: This document is carefully compiled by this editor. I hope that after you download it, it can help you solve practical problems. The document can be customized and modified after downloading, please adjust and use it according to actual needs, thank you! In addition, this shop provides you with various types of practical materials, such as educational essays, diary appreciation, sentence excerpts, ancient poems, classic articles, topic composition, work summary, word parsing, copy excerpts, other materials and so on, want to know different data formats and writing methods, please pay attention!STM32 CAN发送失败处理逻辑在STM32微控制器的CAN通信中,发送数据时可能会出现发送失败的情况。
最新STM32的SPI通信总结(含DMA)
STM32---SPI(DMA)通信的总结(库函数操作)本文主要由7项内容介绍SPI并会在最后附上测试源码供参考:1.SPI的通信协议2.SPI通信初始化(以STM32为从机,LPC1114为主机介绍)3.SPI的读写函数4.SPI的中断配置5.SPI的SMA操作6.测试源码7.易出现的问题及原因和解决方法一、SPI的通信协议SPI(Serial Peripheral Interface)是一种串行同步通讯协议,由一个主设备和一个或多个从设备组成,主设备启动一个与从设备的同步通讯,从而完成数据的交换。
SPI 接口一般由4根线组成,CS片选信号(有的单片机上也称为NSS),SCLK时钟信号线,MISO数据线(主机输入从机输出),MOSI数据线(主机输出从机输入),CS 决定了唯一的与主设备通信的从设备,如没有CS 信号,则只能存在一个从设备,主设备通过产生移位时钟信号来发起通讯。
通讯时主机的数据由MISO输入,由MOSI 输出,输入的数据在时钟的上升或下降沿被采样,输出数据在紧接着的下降或上升沿被发出(具体由SPI的时钟相位和极性的设置而决定)。
二、以STM32为例介绍SPI通信1.STM32f103 带有3个SPI模块其特性如下:2SPI 初始化初始化SPI 主要是对SPI要使用到的引脚以及SPI通信协议中时钟相位和极性进行设置,其实STM32的工程师已经帮我们做好了这些工作,调用库函数,根据自己的需要来修改其中的参量来完成自己的配置即可,主要的配置是如下几项:引脚的配置SPI1的SCLK, MISO ,MOSI分别是PA5,PA6,PA7引脚,这几个引脚的模式都配置成GPIO_Mode_AF_PP 复用推挽输出(关于GPIO的8种工作模式如不清楚请自己百度,在此不解释),如果是单主单从,CS引脚可以不配置,都设置成软件模式即可。
通信参数的设置1.SPI_Direction_2Lines_FullDuplex把SPI设置成全双工通信;2.在SPI_Mode 里设置你的模式(主机或者从机),3.SPI_DataSize是来设置数据传输的帧格式的SPI_DataSize_8b是指8位数据帧格式,也可以设置为SPI_DataSize_16b,即16位帧格式4.SPI_CPOL和SPI_CPHA是两个很重要的参数,是设置SPI通信时钟的极性和相位的,一共有四种模式在库函数中CPOL有两个值SPI_CPOL_High(=1)和SPI_CPOL_Low ( =0). CPHA有两个值SPI_CPHA_1Edge (=0) 和SPI_CPHA_2Edge(=1)CPOL表示时钟在空闲状态的极性是高电平还是低电平,而CPHA则表示数据是在什么时刻被采样的,手册中如下:我的程序中主、从机的这两位设置的相同都是设置成1,即空闲时时钟是高电平,数据在第二个时钟沿被采样,实验显示数据收发都正常。
stm32-串口实验遇到的问题
stm32-串⼝实验遇到的问题
1.Printf函数不能在调试助⼿⾥正常打印?
前提是已经重定向了printf到串⼝,⽽且已经在option⾥勾上了use microlib,⼀切配置都毫⽆问题,在main.c⾥简单printf(“balabala”);却不能在调试助⼿⾥打印出来,点发送也只能发送在调试界⾯输⼊的内容;
2.解决⽅案
(1)将连接电脑的串⼝线,拔⼀下,再插⼀下,点击发送,打印就OK了;
(2)上⾯这种⽅法⽐较笨重,还有⼀种简单的⽅法:直接reset,就会直接答印了;
3.分析
实质上两种⽅法有根本的区别,读者⾃⾏实验判断;由于我是⽤串⼝烧写程序的,在烧写时会关闭调试助⼿的串⼝,等烧写完再打开调试助⼿的串⼝,在这段时间内,⼀条printf打印信息已经被发送完了,但根本没被调试助⼿接收到,所以只要reset⼀下,就会马上打印你想输出的信息了;
4.总结
⼀开始以为是调试助⼿的问题,到处下载其他的调试助⼿,实则结果都⼀样;然后再排查程序的问题(重定向),也没问题;再着查看配置的问题,⽐如引脚的配置,波特率的配置,也都没问题;最后偶然插拔了⼀下usb线就可以了解决问题了;再最后发现reset更为有效。
所以通过以上步骤可以发现,遇到问题,只要⼀⼀排查所有的可能性,最终是会发现答案的。
STM32串口通讯,第一个数据出错问题的排除
STM32串口通讯,第一个数据出错问题的排除STM32串口通讯,第一个数据出错问题的排除使用stm32f10x调试串口通讯时,发现一个出错的现象,硬件复位重启之后,发送测试数据0x01 0x02 0x03 0x04..接收端收到的数据为:0x02 0x03 0x04,第一个数据丢失。
换成发送别的数值的数据,如0x06 0x0ff,则接收到0x0ff,0x06丢失。
错误依旧。
故障排除过程:1、刚开始怀疑是接收端的错误,我是使用电脑串口,运行串口辅助调试工具接收,换成其他软件后,发现故障依旧,而且电脑软件一直是开启状态,不像和电脑软件有关。
2、使用单步调试,单步运行各个发送指令,都正常。
能收到0x01 0x02 0x03 0x04的数据。
间接的排除了不是电脑软件的问题,而是其他的错误。
3、单步调试运行虽然正常了,但连续运行时,错误依旧。
现在有点摸不到头绪了,单步运行正常,看起来编程没有出错,那故障在哪里呢?测试程序如下USART_SendData(USART2,0x01); //Awhile(USART_GetFlagStatus(USART2, USART_FLAG_TC) ==RESET); //BUSART_SendData(USART2,0x02); //Cwhile(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);USART_SendData(USART2, 0x03);while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);USART_SendData(USART2, 0x04);while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);4、猜测,也许是因为某个特殊原因,使第二个数据覆盖了首个数据,使得首个数据丢失。
【STM32】关于UART2发送后中断常犯错误大全,你中招了吗?
【STM32】关于UART2发送后中断常犯错误大全,你中招了吗?先说TC。
即Transmission Complete。
发送一个字节后才进入中断,这里称为“发送后中断”。
和原来8051的TI方式一样,都是发送后才进中断,需要在发送函数中先发送一个字节触发中断。
发送函数如下/*******功能:中断方式发送字符串。
采用判断TC的方式。
即判断发送后中断位。
输入:字符串的首地址输出:无*******/void USART_SendDataString( u8 *pData ){pDataByte = pData;USART_ClearFlag(USART1,USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据。
网友提供。
USART_SendData(USART1,*(pDataByte++)); //必须要++,不然会把第一个字符t发送两次}中断处理函数如下/********* Function Name : USART1_IRQHandler* Description :This function handles USART1 global interrupt request.* Input : None* Output : None* Return : None*********/void USART1_IRQHandler(void){if( USART_GetITStatus(USART1, USART_IT_TC) == SET ){if( *pDa taByte == ‘\0’ )//TC需要读SR+写DR 方可清0,当发送到最后,到‘\0’的时候用个if判断关掉USART_ClearFlag(USART1, USART_FLAG_TC);//不然TC一直是set,TCIE也是打开的,导致会不停进入中断。
clear掉即可,不用关掉TCIEelseUSART_SendData(USART1, *pDataByte++ );}}其中u8 *pDataByte;是一个外部指针变量在中断处理程序中,发送完该字符串后,不用关闭TC的中断使能TCIE,只需要清掉标志位TC;这样就能避免TC == SET 导致反复进入中断了。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
关键词:SMT32105 SPI2 DMA1_Channel5 CRC USART1
现象描述:同时使用SPI2和USART1时,SPI2的数据会异常多发送一个字节数据,实际监控到,每次当USART1接收到固定长度的数据后,SPI2就会自己多发送一帧数据。
问题原因:
1、DMA通道问题:STM32 105的SPI2发送和USART1的接收都归同一个DMA1_Channel5管理,
但是使用时,一个DMA通道下最好只管理一个外设,否则多个设备复用一个通道处理会很复杂,稍微处理不好就会出异常,为了避免复用,笔者只使用DMA1_Channel5管理USART1,但是使用中还是出了问题,这里我们看到DMA1_Channel5下还有其他外设,如果用DMA管理其他外设,SPI2也会出现问题,具体问题我们下面详细描述。
2、SPI设置问题:笔者使用时,SPI作为主设备,全双工模式,通讯时,如果开启了CRC校验,发送数据时就会异常,会莫名奇妙多发送一帧数据,后来发现和USART1的接收有关,其实是因为DMA1_Channel5的管理问题,这里附上SPI2的设置,供大家参考,特殊标注的部分就是出问题的CRC部分,需要关掉才好使。
void SPI2_Init(void)
{
SPI_InitTypeDefSPI_InitStructure;
//DMA_InitTypeDefDMA_InitStructure;
GPIO_InitTypeDefGPIO_InitStructure;
/* Enable SPI2 and GPIO clocks */
/*!< SPI_FLASH_SPI_CS_GPIO, SPI_FLASH_SPI_MOSI_GPIO,
SPI_FLASH_SPI_MISO_GPIO, SPI_FLASH_SPI_DETECT_GPIO
and SPI_FLASH_SPI_SCK_GPIO Periph clock enable */
RCC_APB1PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/*!< SPI_FLASH_SPI Periph clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
/*!< AFIO Periph clock enable */
/*!< Configure SPI_FLASH_SPI pins: SCK */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*!< Configure SPI_FLASH_SPI pins: MISO */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*!< Configure SPI_FLASH_SPI pins: MOSI */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*!< Configure SPI_FLASH_SPI_CS_PIN pin: SPI_FLASH Card CS pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Deselect the FLASH: Chip Select high */
SPI_CS_HIGH();
/* SPI1 configuration */
// W25X16: data input on the DIO pin is sampled on the rising edge of the CLK.
// Data on the DO and DIO pins are clocked out on the falling edge of CLK.
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //两线全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8位数据
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//时钟空闲保持高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //第二个时钟沿采集
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //片选信号由软件产生
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;//SPI_BaudRatePrescaler_8; //8分频,9MHz
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前
SPI_InitStructure.SPI_CRCPolynomial = 0; //
SPI_Init(SPI2, &SPI_InitStructure);
SPI_CalculateCRC(SPI2, ENABLE);//这里,如果Enable CRC功能,SPI发送数据时//就会异常多发数据,这里我们只开启,不设置CRC数据,实际发送时也不控制CRC发送,这样在发送数据时就能看到异常数据。
/* Enable SPI2 */
SPI_Cmd(SPI2, ENABLE);
}
笔者做了测试程序,主从设备都一直发送1-40,一共40个字节数据,不做CRC校验,从设备不开启CRC,主设备也不主动处理和发送CRC数据,只是开启上面的语句,监控SPI总线上的数据,会发现下面的异常波形。
异常数据
通道1中的数据就是主设备发送的SPI数据,通道3中的数据是从设备发送给主设备的数据,可以看到主设备在发送完0x01后,没有继续发送ox02,而是异常的发送了一个0x1A,而从设备的数据是正常的。
当我们把CRC关掉之后,数据就正常了
正常数据
测试发现,这一个字节的异常数据的出现是由串口导致的。
下文我们描述是怎么由串口导致的。
3、串口设置问题:笔者使用时,使用DMA管理串口接收数据,DMA设置的循环接收模式,接收长度固定,使用中,发现,只要接收数据达到了这个设置的固定长度,SPI在发送数据时就会出现上面的异常。
这里不清楚DMA的作用机制,虽然我们没有使用DMA控制SPI,但是只要DMA执行完成一次,就会导致SPI发送了一次CRC数据(上图多发送的数据,仅
笔者猜测,未验证是否是正确的CRC校验和)。
这里,除了笔者使用的串口,可能DMA通
道下,除了SPI的外设,只要触发了MDA完成,都有可能导致SPI发送异常的CRC数据,
原因笔者不清楚,也未查到相关说明,期待高人见解。
处理方法:
1、不使用CRC校验,去除上文测试程序中的CRC使能函数,同时在收发数据前,先清空SPI_DR,清除数据寄存器非空标记,防止总线中异常多进入数据,导致接收真实数据前数据寄存器中已有数据。
2、使用crc时,DMA只用于管理SPI,其他外设不用DMA管理。