完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
单片机的串口用的真的非常多,知识点也多,这里因为最近复习了一下做一个总结 这里的例子我都以103为例,407的话只是串口的配置不一样而已 USART初始化结构体 typedef struct { uint32_t USART_BaudRate; //波特率 uint16_t USART_WordLength; //字长 uint16_t USART_StopBits; //停止位 uint16_t USART_Parity; //校验控制 uint16_t USART_Mode; //模式选择 接收或者发送 uint16_t USART_HardwareFlowControl;//有无硬件流控制 } USART_InitTypeDef; 串口操作相关库函数 void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能 void USART_Cmd();//使能串口 void USART_ITConfig();//使能相关中断 void USART_SendData();//发送数据到串口,DR uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据 FlagStatus USART_GetFlagStatus();//获取状态标志位 void USART_ClearFlag();//清除状态标志位 ITStatus USART_GetITStatus();//获取中断状态标志位 void USART_ClearITPendingBit();//清除中断状态标志位 串口的一般配置步骤 串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd(); 串口复位:USART_DeInit(); 这一步不是必须的 GPIO端口模式设置:GPIO_Init(); 串口参数初始化:USART_Init(); 开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤) NVIC_Init(); USART_ITConfig(); 使能串口:USART_Cmd(); 编写中断处理函数:USARTx_IRQHandler(); 串口数据收发: void USART_SendData();//发送数据到串口,DR uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据 串口传输状态获取: FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG); void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT); 下面以串口一为例 GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; //①串口时钟使能,GPIO 时钟使能,复用时钟使能 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1| RCC_APB2Periph_GPIOA, ENABLE); //使能 USART1,GPIOA 时钟 //②串口复位 USART_DeInit(USART1); //复位串口 1 //③GPIO 端口模式设置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //ISART1_TX PA.9 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.9 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //USART1_RX PA.10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.10 //④串口参数初始化 USART_InitStructure.USART_BaudRate = 115200; //波特率设置 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); //⑤初始化 NVIC NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; //抢占优先级 3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级 3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能 NVIC_Init(&NVIC_InitStructure); //中断优先级初始化 //⑤开启中断 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启中断 //⑥使能串口 USART_Cmd(USART1, ENABLE); //使能串口 中断服务函数 下面我们来逐步分析 标志位 首先是一些重要的状态标志位 单片机发送的时候是先将数据发送到发送数据寄存器,然后在通过发送移位寄存器一位一位发送到TX引脚,接收的时候是通过rx发送到接收移位寄存器,然后再给接收数据寄存器,在给单片机 [tr]标志位作用[/tr]
这里需要理解清楚 发送数据寄存器 TXE为1:TDR里的数据全部到了移位寄存器,并且没有新的数据进TDRTXE为0;TDR里有数据,非空,则TXE=0 发送移位寄存器 TC为1:从TDR过来的数据全部被移送到TX引脚,并且TDR里也没有新的数据TC为0:从TDR过来的数据还没有完全被移过去,或者之前TDR里的数据被移走了,但TDR里又来了新的数据 来几个例子试一下,这里省略了串口的配置,也没有开启中断 USART_SendData(USART1,'d');USART_SendData(USART1,'y');USART_SendData(USART1,'k'); 结果: k 最后串口助手上只打印了一个k 原因:把‘d’发送到发送数据寄存器的时候,因为没有停顿,所以接着发送的‘y’就把‘d’覆盖了,同理可知‘y’也被’k’覆盖了,最后发送到发送移位寄存器,和TX引脚 USART_SendData(USART1,'d');while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);USART_SendData(USART1,'y');while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);USART_SendData(USART1,'k');while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); 结果:dyk 最后串口助手上只打印了dyk 第二个结果不一样是因为加了循环判断标志位,TDR里有数据,非空,则TXE=0,如果为0就会 TXE为1:TDR里的数据全部到了移位寄存器,并且没有新的数据进TDR TXE为0;TDR里有数据,非空,则TXE=0 发送移位寄存器 TC为1:从TDR过来的数据全部被移送到TX引脚,并且TDR里也没有新的数据 TC为0:从TDR过来的数据还没有完全被移过去,或者之前TDR里的数据被移走了,但TDR里又来了新的数据 来几个例子试一下,这里省略了串口的配置,也没有开启中断 USART_SendData(USART1,'d'); USART_SendData(USART1,'y'); USART_SendData(USART1,'k'); 结果: k 最后串口助手上只打印了一个k 原因:把‘d’发送到发送数据寄存器的时候,因为没有停顿,所以接着发送的‘y’就把‘d’覆盖了,同理可知‘y’也被’k’覆盖了,最后发送到发送移位寄存器,和TX引脚 USART_SendData(USART1,'d'); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); USART_SendData(USART1,'y'); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); USART_SendData(USART1,'k'); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); 结果:dyk 最后串口助手上只打印了dyk 第二个结果不一样是因为加了循环判断标志位,TDR里有数据,非空,则TXE=0,如果为0就会一直在循环里面,直到数据发送到发送移位寄存器,才结束循环 最后标志位全部改成TC USART_SendData(USART1,'d'); while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET); USART_SendData(USART1,'y'); while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET); USART_SendData(USART1,'k'); while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET); 最后串口助手上只打印了yk 可以看到TC是先读,再写入,然后就是前面强调的,TXE和TC的复位值为1 所以写完‘d’后直接就退出循环了,‘y’就把‘d’给覆盖了 发现状态标志不同,产生的结果也都不同 然后这里再提一下 USART_GetITStatus和USART_GetFlagStatus的区别 USART_GetITStatus 该函数不仅会判断标志位置1,同时还会判断是否使能了 相应的中断,所以在串口中断函数中,使用该函数 USART_GetFlagStatus 该函数只判断标志位 对发送函数的封装 发送一个字节 void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data) { USART_SendData(pUSARTx, data); while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET ); } 发送一个数组 /* 发送8位数据的数组 */ void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array,uint8_t num) { uint8_t i; for( i=0; i Usart_SendByte(pUSARTx, array); } while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET ); } 发送字符串 /* 发送字符串 */ void Usart_SendStr(USART_TypeDef* pUSARTx, uint8_t *str) { uint8_t i=0; do { Usart_SendByte(pUSARTx, *(str+i)); i++; }while(*(str+i) != ' |