1.串口通讯的双方若采用不同的电平标准,则需要利用电平转换芯片进行转换。
2.调试程序时可以把一些调试信息“打印”在电脑端的串口调试助手上。
3.硬件原理以后有空再研究,应该跟微机里学的挺类似的。。。
1.使能USART时钟,以及RX和TX引脚的GPIO时钟
2.初始化GPIO,配置相关的引脚功能
3.配置USART的工作参数
4.配置中断控制器NVIC,使能串口中断
5.使能USART的接收中断
6.使能USART
7.编写中断服务函数
通用代码参考的是野火的代码,自己写的太不规范了。。。
(1)串口初始化
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
// 打开串口外设的时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 针数据字长
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;
// 完成串口的初始化配置
USART_Init(DEBUG_USARTx, &USART_InitStructure);
/*========================下面三步是配置中断以及使能串口=================*/
// 串口中断优先级配置
NVIC_Configuration();
// 使能串口接收中断
USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
(2)NVIC中断控制器的初始化
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制器组选择 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置USART为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
/* 抢断优先级*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
(3)发送一个字符以及发送字符串
对于发送字符,这里封装了库函数并增加了等待发送完成的查询(若无该步,字符串无法发送成功)
/*===================发送一个字符=========================*/
void Usart_SendByte(USART_TypeDef* USARTx, uint8_t Data)
{
USART_SendData(USARTx,Data);
while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);
}
/*===================发送一个字符串======================*/
void Usart_SendString(USART_TypeDef* USARTx,char *str)
{
uint32_t i=0;
for(;*(str+i)!='\0';i++)
{
Usart_SendByte(USARTx,*(str+i));
}
while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);
}
(4)printf的重定向
为了使用c库中的printf,需要对fputc进行重定向
//注意添加头文件
#include
//……
///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口 */
USART_SendData(DEBUG_USARTx, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
return (ch);
}
///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
/* 等待串口输入数据 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(DEBUG_USARTx);
}
(5)发送和接受数据
定义一个缓冲区,利用中断方式接受数据,利用FIFO模式发送数据,分别贴出中断服务函数以及主函数
//中断服务函数
extern uint8_t Uart_data[64];
extern uint32_t Rx_Num;
void DEBUG_USART_IRQHandler(void)
{
uint8_t temp;
if(USART_GetITStatus(DEBUG_USARTx , USART_IT_RXNE)!=RESET)
{
temp= USART_ReceiveData(DEBUG_USARTx);
Uart_data[Rx_Num]=temp;
Rx_Num++;
Rx_Num&=0x3f;
}
}
//主函数
uint8_t Uart_data[64];
uint32_t Rx_Num=0;
uint32_t Tx_Num=0;
int main(void)
{
DEBUG_USART_Config();
printf("hello,world\r");
while(1)
{
if(Tx_Num!=Rx_Num)
{
USART_SendData(DEBUG_USARTx,Uart_data[Tx_Num]);
Tx_Num++;
Tx_Num&=0x3f;
}
}
}
上述方法定义了三个外部变量,需要注意用法。
(6)设置指令集(控制LED)
利用上位机发送数据时,只能发送单个字符,发送一串字符时仅能接收到第一个。
/*====================字符串匹配=======================*/
uint8_t Cmp(uint8_t *str,uint8_t num)
{
uint8_t i=0;
uint8_t str1[]="111111111";
uint8_t str2[]="000000000";
for(i=0;i<(num-1);i++)
{
if(*(str1+i)!=*(str+i))
break;
}
if(i==(num-1))
return 1;
for(i=0;i<(num-1);i++)
{
if(*(str2+i)!=*(str+i))
break;
}
if(i==(num-1))
return 2;
return 0;
}
//中断服务函数
// 串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
uint8_t ucTemp;
u8 i;
if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
{
ucTemp = USART_ReceiveData(DEBUG_USARTx);
//USART_SendData(DEBUG_USARTx,ucTemp);
Uart_Data[Rx_Num]=ucTemp;
Rx_Num++;
for(i=0;i<10;i++)
{
printf("%c",Uart_Data[i]);
}
if(Rx_Num==9)
{
// for(i=0;i<10;i++)
// {
// printf("%c",Uart_Data[i]);
// }
Rx_Num=0;
}
}
}
//主函数
int main(void)
{
USART_Config();
LED_Config();
while(1)
{
switch(Cmp(Uart_Data,10))
{
case 1: LED1_ON;
LED2_OFF;
break;
case 2: LED1_OFF;
LED2_ON;
break;
case 0:
LED2_OFF;
LED1_OFF;
break;
}
}
}
在使用正点原子例程进行接受数据时,出现的问题
功能概述:MCU之间通过USART2进行数据传输,然后主控芯片又通过USART1向电脑端的串口助手发送数据。
存在问题:在高速数据传输的过程中第一位丢失
原因:printf的问题,重定向时存在问题,导致数据高速传输时,使用printf会导致之后的数据丢失(主控向电脑端传输时)
解决方法:原先用printf发送数据的地方改用USART_SendData发送,在用while等待
while(1)
{
/*====================MCU之间串口通信的数据显示=========================*/
//功能说明
//MCU之间通过USART2进行通信,主控芯片将数据存入缓冲区,并通过串口1发送到电脑端
//数组有效数据长度为9
if(USART2_RX_STA&0x8000) //如果发送完成
{
len=(USART2_RX_STA&0x3fff);
USART_SendData(USART1,len);//发长度
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
for(i=0;i<len;i++) //将数据通过串口1发送
{
USART_SendData(USART1,USART2_RX_BUF[i]);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
}
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
USART_SendData(USART1,'\r');//插入换行
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
USART_SendData(USART1,'\n');//插入换行
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
USART2_RX_STA=0;
}
/*==========================================================================*/
}
注意:一定要取一中间变量len,在for循环发送数据时USART2_RX_STA可能会进入中断从而改变数据长度
正点原子串口数据接收的思路:
1.利用缓冲区
2.设置状态变量USART2_RX_STA,低13位表示数据长度,14位表示接收到0x0d,15位表示接收到0x0a(接收完成)(通信协议,发送的数据最后需要加’\r\n’才能接受)
void USART2_IRQHandler(void) //串口2中断服务程序
{
u8 Res;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
Res =USART_ReceiveData(USART2);//(USART1->DR); //读取接收到的数据
if((USART2_RX_STA&0x8000)==0)//接收未完成
{
if(USART2_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART2_RX_STA=0;//接收错误,重新开始
else USART2_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(Res==0x0d)USART2_RX_STA|=0x4000;
else
{
USART2_RX_BUF[USART2_RX_STA&0X3FFF]=Res ;
USART2_RX_STA++;
if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}