首页 > 编程知识 正文

STM32 自定义串口协议,stm32串口协议

时间:2023-05-04 14:39:07 阅读:179077 作者:974

STM32定制串行协议1串行通信1.1原理及优缺点1.2分类1.2.1按通信方向1.2.2按通信方式1.3异步串行引脚连接1.3.1串行外设间1.3.2 ARM与PC机间1.4图文帧格式1.5西传输原理及优缺点3异步串行通信实例(自定义通信协议) 3.1项目要求3.2项目框架3.3上位机中帧格式3.4代码设计3.5下载文档介绍3.6下载链接3.7说明

设计实现某一功能的系统时,往往需要不同设备之间的通信。 通信主要有串行和并行两种方式,比较广泛的UART (universialasynchonousreceivertransmitter :通用异步收发机,SPI )串行通信文末附有定制通信协议的代码。

1串行通信1.1原理和优缺点数据按位顺序传输,可以使用少数通信线路完成系统之间的信息交换,适用于主机与主机、主机与外围设备之间的远距离通信。 优点是占用的读取资源较少,缺点是速度比并行慢。

1.2分类1.2.1根据通信方向,按通信方向分为单工、半双工、全双工三种。

a、单工:数据传输只支持数据单向传输。

b、半双工:允许数据双向传输。 但是,在某个时刻,数据只能向一个方向传输。 那实际上是切换方向的单工通信。 不需要独立的接收方和发送方,可以将两者组合使用一个端口。

c、全双工:允许数据同时双向传输。 因此,全双工通信是两种单工通信方式的组合,需要独立的接收侧和发送侧。

1.2.2按通信方式分为同步串行和异步串行。

同步串行:在同步通信的情况下,通信双方共享一个时钟是同步通信区别于异步通信的最明显的特征。 在同步通信中,在开始数据传输之前用同步字符进行指示,通过时钟实现发送方和接收方的同步。 这意味着一旦检测到给定的同步字符,就按顺序继续传输数据,直到下一个数据被传输。 同步传输时,字符之间没有间隙。 也不需要起始位和停止位。 仅在数据开始时用同步字符SYNC指示。 例如SPI、I2C通信接口。

异步串行:无时钟信号驱动,收发双方约定的数据帧格式。 各帧由开始位、数据位、奇偶校验位、停止位构成。 在传输过程中的字符之间可以有间隙。 例如UART,单巴士。

1.3异步串行端连接RXD :数据输入端,数据接收。

TXD )数据发送端子、数据发送。

1.3.1串行外围设备之间

1.3.2 ARM与PC之间

ARM连接到电脑的USB端口并传输数据时,需要USB向串行端口传输的装置。 电脑无法识别TTL等级,需要连接RS232转换器将TTL等级转换为232等级。 经常采用的是MAX232或国产的CH340芯片。

TL水平232水平高1:2.0V ~ 5V高1: -15V ~ -13V低0:0V ~ 0.8V低0:15v~13vlvttl,高低水平范围分别为2.45v、0-- 0.40.45 v

更多的RS232级连接方式和知识值得借鉴:链接1

1.4字符帧格式的起始比特数据位奇偶校验:奇偶校验是指保证8比特数据位中1的个数为基数,如果1为基数,则校验位为0,反之亦然; 偶数检查是指保证8位数据位中1的个数为偶数个,如果1为偶数个,则检查位为0,反之为1;

停止位速率:波特率是指在传输数据时每秒传输的数据二进制位数,单位为位/秒(bit/s )。 在异步串行通信中,接收设备和发射设备保持相同的传输波特率,并在每个字符数据的第一位与发射设备保持同步。 开始位。 数据位。 奇偶校验位和停止位约定在同一传输过程中必须一致才能正常传输数据。 1.5串行通信流程

1.6串口框图详细图片可查看STM32F4数据手册26.3节。 文末的下载链接有STM32F4中文数据手册。

2并行通信2.1传输原理和优缺点数据位被同时传输,高速设备之间采用并行通信。 例如,CPU和存储设备、存储器和存储器、主机和打印机等采用并行通信。 在并行通信中,有多少位数据就需要多少根数据线? 具有传输速度快、效率高、多用于要求实时性的场合的优点。 缺点是长距离通信时的抗干扰能力差,占用很多读取资源。 因为并行通信APP应用很少,所以这里不多介绍。

3异步串行实例(自定义通信协议)说明)本实例适用于具体项目需要自定义帧头、帧尾等数据传输格式的APP。 基本的串口设置步骤,如使能时钟、初始化参数等在此就不多介绍了,具体请参考满意的大山STM32F4开发指南《库函数版》-5.3.3节和第9章《串口通信实验》。

另请参阅链路2,了解串行端口如何接收数据

因为涉及到具体的项目,所以不透露太多细节,只介绍通信协议,方便大家移植。 转入正题。

3.1项目

需求

系统分为三个模块,ARM(STM32F4)、FPGA、模拟电路。具体的系统功能由模拟电路实现。ARM负责接收来自上位机的指令,并控制FPGA输出命令信息和通信信息。

3.2 项目框架

上位机与ARM通过板载的蓝牙通信,采用了HC-05蓝牙转串口模块,模块的引脚RXD与TXD分别与ARM的串口收发端相连接,这样即可实现上位机与ARM的通信。
ARM收到正确的上位机指令后,将指令或数据按照通信协议发送给FPGA,由FPGA控制模拟电路完成系统功能。

连接方式如下图所示,在图示的基础上,还要将HC-05模块的AT指令设置脚连接到ARM的IO上,AT指令用来设置蓝牙模块的参数,如设备名,密码,波特率之类。尤其要注意的是要设置蓝牙的波特率与程序中设置的串口波特率一样。设置方法见文末下载链接里的文档—HC-05芯片手册。

3.3 上位机下传帧格式


说明:如果是自定义帧头的话,尽量选择55,AA之类不容易在数据部分出现的码,或者是用两个字节充当帧头。
校验和为开始标识+命令号+数据长度+数据(16进制求和)。表中省略号的部分……当然是懒得算啦,一个一个加也太傻了。分享一个计算校验和的网站:链接3
当然帧头/尾、数据长度、数据、校验和这些你可以任意安排,只要上位机按照这种数据格式发送数据,作为处理器端按这种数据格式接收数据就行。这就是双方的通信协议。
数据长度与校验和都是高位在前,低位在后发送。例如命令号为01,没有数据的上位机指令为:C1D20100000194E3F4

3.4 代码设计

当上位机指令有错误时,ARM会返回对应的错误号码,上位机发送指令正确,则会返回上位机发送的数据(只是帧格式中的数据,不包含命令号等其他的)。发送上位机指令用的是串口助手,串口助手设置见下图,串口助手的参数一定要跟ARM程序中设置的一样,就像两个人能正常交流那就得用约定好的语言规则。
不同指令运行结果见下图:


开发板型号:正点原子F4探索者
使用芯片:STM32F40ZGT6
开发环境:IAR

//串口初始化void uart_init(u32 bound){ //GPIO端口设置 GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟 //串口1对应引脚复用映射 GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1 GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1 //USART1端口配置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10 //USART1 初始化设置 USART_InitStructure.USART_BaudRate = bound;//波特率设置 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式 USART_Init(USART1, &USART_InitStructure); //初始化串口1 USART_Cmd(USART1, ENABLE); //使能串口1 USART_ClearFlag(USART1, USART_FLAG_TC); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断 //Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;//子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能 NVIC_Init(&NVIC_InitStructure);//根据指定的参数初始化VIC寄存器、}

下面是串口一的中断服务函数,前面设置了字长为8位,串口的收发也是以一个字节为基本单位进行,设计思路是每接收一帧数据,会根据数据下标N来判断数据是否正确,正确则继续接收,错误则返回错误编号且初始化变量。全部接收正确则进入下一个状态。

//串口1中断服务程序void USART1_IRQHandler(void) { unsigned int rec_data; if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断 RXNE是准备好读取接收到的数据标志位 { USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除接受中断标志 if(N==0) my_chk=0;//初始化和校验 rec_data = USART_ReceiveData(USART1);//赋值临时变量 my_chk+=rec_data;//计算和校验 //检验开始标识 if(N==0) { if(rec_data!=0xC1) { commu_err = PROT_W; } } else if(N==1) { if(rec_data!=0XD2) { commu_err = PROT_W; } } //检验命令标识 else if(N==2) { if( rec_data!=0x01 && rec_data!=0x02 && rec_data!=0x03 && rec_data!=0x04 && rec_data!=0x05 && rec_data!=0x06 ) { commu_err = CODE_W; } else command=rec_data; } //接收数据长度 2Byte else if(N==3){ rec_length=rec_data; } else if(N==4) { rec_length = (rec_length << 8) + (rec_data); //合并数据长度 if(rec_length>599) { commu_err = PROT_W; } } else { //接收数据 if(N<=rec_length+4) { //此处可根据不同命令号的数据长度不同,设置不同的N值筛选条件 USART_RX_BUF[N-5]=rec_data; } //接收和校验 2Byte else if(N==rec_length+5) { rec_chk=rec_data; } else if(N==rec_length+6) { my_chk-=rec_chk;//减去接收到的校验和低位 my_chk-=rec_data;//减去接收到的校验和高位 rec_chk = (rec_chk << 8) + rec_data; //合并校验和 if(rec_chk != my_chk) //判断接收到的校验和与计算出来的校验和是否相等 { commu_err = SUMM_W; } } //接收结束标识 else if(N==rec_length+7) { if(rec_data!=0xE3) { commu_err = PROT_W; } } else if(N==rec_length+8) { if(rec_data!=0xF4) { commu_err = PROT_W; } else { P2A_Flag = 1; //上位机发送数据符合协议,接收完毕进入下一个状态 } } } N++; if( commu_err == PROT_W || commu_err == CODE_W || commu_err == SUMM_W ) { while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET); //测试用,正式版本删除 USART_SendData(USART1,commu_err); Init_USART_RX_BUF(); Init_BUF(); } }}*主函数中判断接收是否完成,完成后可以进入下一个状态。*```main.cint main(void){ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2 delay_init(168);//延时初始化 uart_init(38400);//串口初始化,波特率为38400 Init_USART_RX_BUF(); //初始化接受寄存器(上位机发送到ARM的数据存储在此数组) Init_BUF(); //过程中的变量初始化 while(1) { if(P2A_Flag == 1 ) { for(j=0;j<rec_length;j++) //测试用,正式版本删除 { while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET); USART_SendData(USART1,USART_RX_BUF[j]); } Init_BUF(); Init_USART_RX_BUF(); } }} 3.5 下载文档介绍

文末下载链接内的文档如下图所示:


编号06的文档是本博客的word版,包含除了代码外的所有文字部分。

3.6 下载链接

链接4

3.7 说明

本项目中的ARM和FPGA通信同样采用了自定义协议通信的方式,效率不高且占用IO过多,下个项目采用了SPI通信,等功能完善的差不多了,我会再写一篇ARM与FPGA进行SPI通信的文章。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。