离线
TA的每日心情 | 慵懒 2021-7-23 17:16 |
---|
签到天数: 17 天 [LV.4]
|
有人预言,RISC-V或将是继Intel和Arm之后的第三大主流处理器体系。欢迎访问全球首家只专注于RISC-V单片机行业应用的中文网站
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
本帖最后由 草帽王子 于 2021-9-10 17:43 编辑
本章教程主要在SPI通信方式下使用CRC校验以保证通信的可靠性。
1、SPI简介及相关函数介绍
关于SPI CRC校验,其用于保证全双工通信的可靠性。数据的发送和接收分别使用单独的CRC计算器。通过对每一个接收位进行可编程的多项式运算来计算CRC。CRC的计算是在由SPI控制寄存器1(SPIx_CTLR1)中的CPHA和CPOL位定义的采样时钟边沿进行的。
注意:该SPI接口提供了两种CRC计算方法,取决于所选的发送和/或接收的数据帧格式:8位数据帧采用CR8;16位数据帧采用CRC16。
CRC计算是通过设置SPI控制寄存器1(SPIx_CTLR1)中的CRCEN位启动的。设置CRCEN位时同时复位SPI接收CRC寄存器和SPI发送CRC寄存器(SPIx_RCRCR和SPI_TCRCR)。当设置了SPI控制寄存器1(SPIx_CTLR1)的CRCNEXT位, SPI_TCRCR的内容将在当前字节发送之后发出。
在传输SPI_TCRCR的内容时,如果在移位寄存器中收到的数值与SPI_RCRCR的内容不匹配,则SPI_SR寄存器的CRCERR标志位被置1。
如果在TX缓冲器中还有数据, CRC的数值仅在数据字节传输结束后传送。在传输CRC期间,CRC计算器关闭,寄存器的数值保持不变。
SPI通信可以通过以下步骤使用CRC:
● 设置CPOL、CPHA、LSBFIRST、BR、SSM、SSI和MSTR的值;
● 在SPI_CRCR寄存器输入多项式;
● 通过设置SPIx_CTLR1寄存器CRCEN位使能CRC计算,该操作也会清除寄存器SPI_RCRCR和SPI_TCRCR;
● 设置SPIx_CTLR1寄存器的SPE位启动SPI功能;
● 启动通信并且维持通信,直到只剩最后一个字节或者半字;
● 在把最后一个字节或半字写进发送缓冲器时,设置SPIx_CTLR1的CRCNEST位,指示硬件在发送完成最后一个数据之后,发送CRC的数值。在发送CRC数值期间,停止CRC计算;
● 当最后一个字节或半字被发送后,SPI发送CRC数值, CRCNEST位被清除。同样,接收到的CRC与SPI_RCRCR值进行比较,如果比较不相配,则设置SPI_SR上的CRCERR标志位,当设置了SPIx_CTLR2寄存器的ERRIE时,则产生中断。
注意:当SPI模块处于从设备模式时,请注意在时钟稳定之后再使能CRC计算,否则可能会得到错误的CRC计算结果。事实上,只要设置了CRCEN位,只要在SCK引脚上有输入时钟,不管SPE位的状态,都会进行CRC的计算。
当SPI时钟频率较高时,用户在发送CRC时必须小心。在CRC传输期间,使用CPU的时间应尽可能少;为了避免在接收最后的数据和CRC时出错,在发送CRC过程中应禁止函数调用。必须在发送/接收最后一个数据之前完成设置CRCNEXT位的操作。
当SPI时钟频率较高时,因为CPU的操作会影响SPI的带宽,建议采用DMA模式以避免SPI降低的速度。
当CH32V103配置为从模式并且使用了NSS硬件模式,NSS引脚应该在数据传输和CRC传输期间保持为低。
当配置SPI为从模式并且使用CRC的功能,即使NSS引脚为高时仍然会执行CRC的计算(译注:当NSS信号为高时,如果SCK引脚上有时钟脉冲,则CRC计算会继续执行)。例如:当主设备交替与多个从设备进行通信时,将会出现这种情况(译注:此时要想办法避免CRC的误操作)。
在不选中一个从设备(NSS信号为高)转换到选中一个新的从设备(NSS信号为低)的时候,为了保持主从设备端下次CRC计算结果的同步,应该清除主从两端的CRC数值。
按照下述步骤清除CRC数值:
1. 关闭SPI模块(SPE=0);
2. 清除CRCEN位为’0’;
3. 设置CRCEN位为’1’;
4. 使能SPI模块(SPE=1)。
2、硬件设计
本章教程主要在第46章基础上进行,需用到两个开发板,主设备使用MOSI引脚,从设备使用MISO引脚进行通讯。
此处使用外设为SPI1,主设备MOSI对应引脚为PA7引脚,从设备MISO对应引脚为PA6引脚,将主设备PA7引脚与从设备PA6引脚连接起来,此外还需将两个开发板SPI1对应的SCK引脚PA5连接起来。
此外,由于两个开发板需要同时进行上电传输,因此将两个开发板的3.3V引脚和GND引脚进行连接。
3、软件设计
本章教程主要在第46章基础上进行,增加了CRC校验步骤,具体程序如下:
spi.h文件
- #ifndef __SPI_H
- #define __SPI_H
- #include "ch32v10x_conf.h"
- /* SPI Mode Definition */
- #define HOST_MODE 0
- #define SLAVE_MODE 1
- /* SPI Communication Mode Selection */
- #define SPI_MODE HOST_MODE
- //#define SPI_MODE SLAVE_MODE
- #define Size 4
- extern u16 TxData[Size];
- extern u16 RxData[Size];
- void SPI_1Lines_HalfDuplex_Init(void);
- #endif
复制代码
spi.c文件
- #include "spi.h"
- /* Global Variable */
- u16 TxData[Size] = { 0x01, 0x02, 0x03, 0x04};
- u16 RxData[Size];
- /*******************************************************************************
- * Function Name : SPI_1Lines_HalfDuplex_Init
- * Description : Configuring the SPI for half-duplex communication.
- * Input : None
- * Return : None
- *******************************************************************************/
- void SPI_1Lines_HalfDuplex_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- SPI_InitTypeDef SPI_InitStructure;
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE );
- #if (SPI_MODE == HOST_MODE)
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init( GPIOA, &GPIO_InitStructure );
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init( GPIOA, &GPIO_InitStructure );
- #elif (SPI_MODE == SLAVE_MODE)
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
- GPIO_Init( GPIOA, &GPIO_InitStructure );
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
- GPIO_Init( GPIOA, &GPIO_InitStructure );
- #endif
- #if (SPI_MODE == HOST_MODE)
- SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
- SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
- #elif (SPI_MODE == SLAVE_MODE)
- SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Rx;
- SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
- #endif
- SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
- SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
- SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
- SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
- SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
- SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
- SPI_InitStructure.SPI_CRCPolynomial = 7;
- SPI_Init( SPI1, &SPI_InitStructure );
- SPI_CalculateCRC( SPI1, ENABLE ); //开启CRC计算
- SPI_Cmd( SPI1, ENABLE );
- }
复制代码
spi.c文件主要包括1个函数:SPI_1Lines_HalfDuplex_Init函数。SPI_1Lines_HalfDuplex_Init函数主要进行主机发送从机接收配置。
在这种配置下,主机需要用到SCK引脚和MOSI引脚,从机需要用到SCK引脚和MISO引脚,此处,由于用到SPI1,对应SCK引脚为PA5,MOSI引脚为PA7,MISO引脚为PA6,因此,在SPI_1Lines_HalfDuplex_Init函数中首先对主机和从机对应GPIO引脚进行初始化配置,此处需要注意,由于是主机发送从机接收,需要将PA7设置为复用推挽输出模式,PA6设置为浮空输入模式。
此外,由于主机作为发送,从机作为接收,此处在SPI_1Lines_HalfDuplex_Init函数中需要对SPI进行主机发送和从机接收初始化配置,此配置可根据CH32V103应用手册主模式和从模式配置步骤进行,主要对SPI通信的通信方向、主从模式、数据帧大小、时钟极性、时钟相位、NSS引脚使用方式、波特率等进行配置,可对照手册参考标准库函数ch32v10x_spi.c文件中SPI_Init函数进行配置。
main.c文件
- /********************************** (C) COPYRIGHT *******************************
- * File Name : main.c
- * Author : WCH
- * Version : V1.0.0
- * Date : 2020/04/30
- * Description : Main program body.
- *******************************************************************************/
- #include "debug.h"
- #include "spi.h"
- /*
- *@Note
- 使用CRC错误校验,Master/Slave 模式收发例程:
- Master:SPI1_SCK(PA5)、SPI1_MOSI(PA7)。
- Slave :SPI1_SCK(PA5)、SPI1_MISO(PA6)。
- 本例程演示使用CRC错误校验,Master 发,Slave 收。
- 注:两块板子分别下载 Master 和 Slave 程序,同时上电。
- 硬件连线:PA5 —— PA5
- PA7 —— PA6
- */
- /*******************************************************************************
- * Function Name : main
- * Description : Main program.
- * Input : None
- * Return : None
- *******************************************************************************/
- int main(void)
- {
- u8 i=0,crcval;
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
- Delay_Init();
- USART_Printf_Init(115200);
- printf("SystemClk:%d\r\n",SystemCoreClock);
- SPI_1Lines_HalfDuplex_Init();
- #if (SPI_MODE == SLAVE_MODE)
- printf("SLAVE Mode\r\n");
- Delay_Ms(1000);
- #endif
- #if (SPI_MODE == HOST_MODE)
- printf("HOST Mode\r\n");
- Delay_Ms(2000);
- #endif
- while(1)
- {
- #if (SPI_MODE == HOST_MODE)
- while( i<4 )
- {
- if( i<3 )
- {
- if( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_TXE ) != RESET ) //发送缓冲区非空
- {
- SPI_I2S_SendData( SPI1, TxData[i] ); //发送数据
- i++;
- }
- }
- else
- {
- if( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_TXE ) != RESET ) //发送缓冲区非空
- {
- //SPI_TransmitCRC函数通过控制SPIx_CTLR1寄存器的CRCNEXT位实现,当将CRCNEXT位置1,SPIx_TCRCR寄存器的内容将在当前字节发送之后发出。
- SPI_TransmitCRC( SPI1 );
- SPI_I2S_SendData( SPI1, TxData[i] ); //发送数据
- i++;
- }
- }
- }
- while( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_TXE ) != RESET ) //发送缓冲区非空
- {
- if(SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_BSY ) == RESET) //SPI不在通讯状态
- {
- SPI_Cmd( SPI1, DISABLE );
- crcval = SPI_GetCRC( SPI1, SPI_CRC_Tx ); //获取SPI1发送CRC寄存器的值
- printf( "CRC:%02x\r\n", crcval );
- while(1);
- }
- }
- #elif (SPI_MODE == SLAVE_MODE)
- while( i<4 )
- {
- if( i<3 )
- {
- if( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_RXNE ) != RESET ) //接收缓冲区为空
- {
- RxData[i] = SPI_I2S_ReceiveData( SPI1 ); //接收数据
- i++;
- }
- }
- else if( i == 3)
- {
- //在发送或者接收最后一个数据之前需将SPI控制寄存器1(SPIx_CTLR1)CRCNEXT位置1
- SPI_TransmitCRC( SPI1 );
- if( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_RXNE ) != RESET )
- {
- RxData[i] = SPI_I2S_ReceiveData( SPI1 );
- i++;
- }
- }
- else
- {
- if( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_RXNE ) != RESET )
- {
- RxData[i] = SPI_I2S_ReceiveData( SPI1 );
- i++;
- }
- }
- }
- crcval = SPI_GetCRC( SPI1, SPI_CRC_Rx ); //获取SPI1接收CRC寄存器的值
- printf( "CRC:%02x\r\n", crcval );
- for( i=0; i<4; i++ )
- {
- printf( "Rxdata:%02x\r\n", RxData[i] );
- }
- while(1);
- #endif
- }
- }
复制代码 main.c文件主要进行主机和从机下的数据发送和接收。并将接收数据与发送数据进行对比,当发送数据与接收数据相同,输出same,若不同,输出different。
4、下载验证
将编译好的程序分别在主机模式和从机模式下下载到两个开发版,并将主机的引脚与从机的引脚一一对应进行连接,开发板上电后,串口打印如下:
主机打印:
从机打印:
51、SPI-CRC.rar
51、SPI-CRC.rar
(467.53 KB, 下载次数: 11)
链接:https://pan.baidu.com/s/14dC407_qxwYiItx1qGf-xQ
提取码:w5fz
复制这段内容后打开百度网盘手机App,操作更方便哦
完
|
上一篇: 第五十一章:SPI-全双工通信,硬件控制NSS模式下一篇: 五十三章:CH32V103应用教程——SPI-DMA
|