完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
一、串口的初始化和中断设置
1、初始化GPIO: 根据手册的8.1.11节,我们可以找到下表: 在全双工的模式下,发送引脚需要设置为推挽复用输出,接收引脚则设置为浮空输入或带上拉的输入。因为一般不用同步和流量控制的方式,所以CK、RST、CTS引脚不作配置。当然啦,在使用STM32外设的时候不要忘记打开外设时钟(GPIO和USART的RCC)。 1. USART_FLAG_RXNE—接受数据寄存器非空标志位(Receive data register not empty flag) 当RDR移位寄存器中的数据被转移到USART_DR寄存器中,该位被硬件置位 (表示接受到一个字节) 。如果USART_CR1寄存器中的RXNEIE为1,则产生中断。对USART_DR的读操作可以将该位清零。RXNE位也可以通过写入0来清除,只有在多缓存通讯中才推荐这种清除程序。 0:数据没有收到; 1:收到数据,可以读出。 2. USART_FLAG_TXE —发送数据寄存器空标志位(Transmit data register empty flag) 当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位 (表示发送数据寄存器为空,可以继续写入数据。注:此时并不确定数据是否发送完成) 。如果USART_CR1寄存器中的TXEIE为1,则产生中断。对USART_DR的写操作,将该位清零。 0:数据还没有被转移到移位寄存器; 1:数据已经被转移到移位寄存器。 3. USART_FLAG_TC — 发送完成标志位(Transmission Complete flag) 包含有数据的一帧发送完成后,并且TXE=1时,由硬件将该位置’1’ (表示一帧数据发送完成,可以继续发送下一帧)。如果USART_CR1中的TCIE为’1’,则产生中断。由软件序列清除该位(先读USART_SR,然后写入USART_DR)。TC位也可以通过写入’0’来清除,只有在多缓存通讯中才推荐这种清除程序。 0:发送还未完成; 1:发送完成。 有一种形象的说法是:TXE是指“弹仓”空;TC是“枪膛”空。 也就是说,你写数据到串口时,是装入弹仓,硬件会将数据移到枪膛,这时,TXE为1,TC为0,STM32硬件的TX脚正在发送数据,但你还可以装入数据到弹仓,装入后,TXE为0,TC为0. TX发送完一个数据后,立即将数据从弹仓移入枪膛,这时,TXE为1,TC为0. 最后TX发送完数据,你又没有装入新数据,这时。TXE为1,TC为1. GPIO_InitTypeDef GPIO_InitStructure; //开启串口和GPIO的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); //配置发送引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //发送引脚设置为推挽复用 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_10; //接收引脚设置为浮空输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); 2、配置串口参数 有专门用于初始化串口的库函数(USART_Init)和对应的结构体(USART_InitTypeDef),好像每个外设都有这样的配套,具体内容可参看《STM32F10xxx固件库_3.xx.pdf》。 USART_InitTypeDef USART_InitStructure; //波特率 USART_InitStructure.USART_BaudRate = 9600; //数据长度 USART_InitStructure.USART_WordLength = USART_WordLength_8b; //停止位 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; //初始化串口1 USART_Init(USART1, &USART_InitStructure); 3、中断配置 在使用STM32的中断前,要对NVIC中断控制器进行配置,设置中断的优先级。 //配置中断优先级 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); 4、使能串口及串口中断 注意1:初始化时不要随意打开TXE中断!只要TX-DR寄存器为空,TX和TXE标志都会马上被置位而立即会产生中断(参考《STM32中文参考手册》的25.3.2节),即使中断标志被清除,也会被重新置位。因此,我采用的是TC中断而不是采用TXE中断。 注意2:不要采用在一个中断配置函数中同时打开两个中断!例如:USART_ITConfig(USART1, USART_IT_TC | USART_IT_RXNE, ENABLE); 咋眼一看,明明只打开TC中断和RX中断,然而却会同时把TXE中断也打开。 //串口1使能 USART_Cmd(USART1, ENABLE); //清除接收中断标记 USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除发送完成中断标记 USART_ClearITPendingBit(USART1, USART_IT_TC); //打开串口1发送完中断 USART_ITConfig(USART1, USART_IT_TC, ENABLE); //打开串口1接收中断 两个中断不能在一个函数中同时打开!!!太坑了T_T USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); ———————————————— 这样,串口1配置好了。但代码一运行就会发现不妥!为什么每次初始化完成就马上进入中断了呢???遇到这种现象千万不要大惊小怪,我很淡(dan)定(teng)地做了个实验,发现处理器复位后,串口的SR寄存器中的TC标志会被置位。而根《STM32中文参考手册》25.3.2节,在串口使能后会自动发送一个空闲帧,发送完毕后TC也会置位,所以初始化将导致串口初始化完毕后马上进入TC中断。为了避免这种情况,可以在串口使能后等待空闲帧发送完毕,再打开TC中断。 具体看下面完整的初始化代码: ———————————————— //配置串口1 void USART1_Config() { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); //配置发送引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //发送引脚设置为推挽复用 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_10; //接收引脚设置为浮空输入 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); //波特率 USART_InitStructure.USART_BaudRate = 9600; //数据长度 USART_InitStructure.USART_WordLength = USART_WordLength_8b; //停止位 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; //初始化串口1 USART_Init(USART1, &USART_InitStructure); //USART1->SR寄存器复位后TC位为1,在此清零 USART_ClearFlag(USART1, USART_FLAG_TC); //串口1使能 USART_Cmd(USART1, ENABLE); //使能后串口发送一个空闲帧,等待空闲帧发送完毕后将TC标记位清零 while(USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET); //否则开启TC中断后会马上中断 USART_ClearFlag(USART1, USART_FLAG_TC); //配置中断优先级 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /*************************************************************** 注意:初始化时不要随意打开TXE中断, 因为只要TX-DR寄存器为空,TX和TXE都会马上被置位而立即会产生中断 ***************************************************************/ //清除接收中断标记 USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除发送完成中断标记 USART_ClearITPendingBit(USART1, USART_IT_TC); //打开串口1发送完中断 USART_ITConfig(USART1, USART_IT_TC, ENABLE); //打开串口1接收中断 两个中断不能在一个函数中同时打开!!!太坑了T_T USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); } ———————————————— 二、数据的发送和接收(注:以下代码有bug,博主至今还未找到原因T-T,仅供思路以参考!) 为了提高代码的效率,我使用基于环形缓冲的串口通信方式。 发送数据原理:把要发送的数据全部加入到缓冲区中,让处理器开始发送。一个数据发送结束后,即会产生TC中断,此时在中断服务程序中发送下一个数据。像吃饭看电视,在夹菜(发数据)的时候才要把注意力放到菜盘子上,嚼饭的时候(数据发送中)可以看电视,在开始发送数据到数据发送完毕触发中断的这段时间里,处理器可以去做别的事情。 接收数据原理:当一个数据接收完毕后,将数据立存入缓冲区而不处理,并在未处理数据的计数器上加1。等到处理器空闲,再从缓冲区读取这些数据做并处理(不在中断函数中)。 如此一来,串口的收发速率并不受影响,还能保证处理器在数据收发的过程中并行执行其他任务。 ———————————————— #include "usart1.h" #include "string.h" //发送缓冲区 u8 Usart1_SendBuffer[USART_SendBufferSize]; //接收缓冲区 u8 Usart1_RecvBuffer[USART_RecvBufferSize]; //发送缓冲区指针 int Usart1_SendPointer = 0; //接收缓冲区指针 int Usart1_RecvPointer = 0; //发送字符队列的长度 int Usart1_SendDataSize = 0; //接收未处理字符数 int Usart1_RecvDataSize = 0; //串口1发送状态 int Usart1_SendStatus = USART_Status_Idle; //生成字符串的缓冲区 char StringBuffer[100]; //发送字符串 void USART1_SendString(char *str) { // while(*str) // { // USART1_SendByte(*str++); // } USART1_SendArray((u8*)str, strlen(str)); } //发送字节队列 void USART1_SendArray(u8 *DataArray, int count) { if(count <= 0) { return; } while(count) { USART1_SendByte(*DataArray++); count --; } } //发送一个字节 void USART1_SendByte(u8 data) { int pos; //如果缓冲区满了,要等待 while(Usart1_SendDataSize >= USART_SendBufferSize); //计算数据在缓冲区的位置 pos = Usart1_SendPointer + Usart1_SendDataSize; //数据位置超过缓冲区尾地址 if(pos >= USART_SendBufferSize) { //重新计算位置 pos = pos - USART_SendBufferSize; } Usart1_SendBuffer[pos] = data; Usart1_SendDataSize ++; //如果串口空闲,立即发送 if(Usart1_SendStatus == USART_Status_Idle) { Usart1_SendStatus = USART_Status_Busy; USART_SendData(USART1, Usart1_SendBuffer[Usart1_SendPointer++]); //指针移动到缓冲区尾地址后,循环到缓冲区首地址 if(Usart1_SendPointer == USART_SendBufferSize) { Usart1_SendPointer = 0; } Usart1_SendDataSize --; } } //串口1中断服务程序 void USART1_IRQHandler() { //判断发送完成中断 if(USART_GetITStatus(USART1, USART_IT_TC) == SET) { //清空发送完成TC标记位 USART_ClearFlag(USART1, USART_FLAG_TC); //清空串口发送完成中断TCIE标记 USART_ClearITPendingBit(USART1, USART_IT_TC); if(Usart1_SendDataSize > 0) { //发送下一个数据 USART_SendData(USART1, Usart1_SendBuffer[Usart1_SendPointer++]); //指针移动到缓冲区尾地址后,循环到缓冲区首地址 if(Usart1_SendPointer == USART_SendBufferSize) { Usart1_SendPointer = 0; } //待发送数据减1 Usart1_SendDataSize --; } else { //发送完毕,串口1发送状态:空闲 Usart1_SendStatus = USART_Status_Idle; } } //接收中断 if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET) { //清空串口接收标记 USART_ClearITPendingBit(USART1, USART_IT_RXNE); //获取缓冲区数据 Usart1_RecvBuffer[Usart1_RecvPointer++] = USART_ReceiveData(USART1); //如果没有溢出,待处理数据+1。否则丢弃该数据 if(Usart1_RecvDataSize < USART_RecvBufferSize) { Usart1_RecvDataSize ++; } //指针移动到缓冲区尾地址后,循环到缓冲区首地址 if(Usart1_RecvPointer == USART_RecvBufferSize) { Usart1_RecvPointer = 0; } } } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1570 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1515 浏览 1 评论
943 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
669 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1562 浏览 2 评论
1851浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
620浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
506浏览 3评论
511浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
492浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-13 13:12 , Processed in 0.659259 second(s), Total 44, Slave 39 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号