STM32串口通信-简单版

STM32串口通信-简单版

参考原子串口通信的程序,编写一个STM32串口简单版的程序,巩固其原理以及要注意的点

串口初始化

初始化的部分都一样,但先不用中断,所以不用初始化NVIC

/**
 * @name   USART1_Init
 * @brief  延时初始化
 * @param  None
 * @retval None
 */
void USART1_Init(u32 bound)
{
    GPIO_InitTypeDef GPIO_InitStruc;
    USART_InitTypeDef USART_InitStruc;
    //初始化GPIO和串口时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);

    //初始化PA9,接到TX
    GPIO_InitStruc.GPIO_Mode = GPIO_Mode_AF_PP;    //设置为推挽输出
    GPIO_InitStruc.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruc.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA,&GPIO_InitStruc);

    //初始化PA10,接到RX
    GPIO_InitStruc.GPIO_Mode = GPIO_Mode_IN_FLOATING;    //设置为浮空输入
    GPIO_InitStruc.GPIO_Pin = GPIO_Pin_10;

    GPIO_Init(GPIOA,&GPIO_InitStruc);

    //初始化串口
    USART_InitStruc.USART_BaudRate = bound;         //波特率
    USART_InitStruc.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件控制流
    USART_InitStruc.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;   //发送和接收模式
    USART_InitStruc.USART_Parity = USART_Parity_No; //无奇偶校验位
    USART_InitStruc.USART_StopBits = USART_StopBits_1;  //一位停止位
    USART_InitStruc.USART_WordLength = USART_WordLength_8b; //8位数据位

    USART_Init(USART1,&USART_InitStruc);

    //串口1使能
    USART_Cmd(USART1,ENABLE);

}

发送一个字符

初始化后,就可以在主函数中进行发送和接收操作了,这里是个最简单的发送操作,调用USART_SendData函数往串口1发送一个字符

开发板上电后,就往串口发送字符H

int main()
{
	u8 temp;
	//延时初始化
	delay_Init();
	//串口初始化
	USART1_Init(9600);
	USART_SendData(USART1,'H');
	while(1)
	{		
	}
}

如果连续调用USART_SendData函数发送数据,则会出现问题,如下面程序

int main()
{
	u8 temp;
	//延时初始化
	delay_Init();
	//串口初始化
	USART1_Init(9600);
	USART_SendData(USART1,'H');
    USART_SendData(USART1,'E');
    USART_SendData(USART1,'L');
    USART_SendData(USART1,'K');
	while(1)
	{		
	}
}

上电执行,串口助手只收到字符K,多个K是按了多次复位键产生的,刚上电时才只有一个K

在这里插入图片描述

这是因为程序执行太快,USART_SendData( )只是把数据放入到TDR寄存器中,数据还没来得及放入发送移位寄存器中,下一个数据就将DR寄存器的值给覆盖了,所以最后只有一个K字符没被覆盖,被转移到了移位寄存器中,再发送了出去
在这里插入图片描述

修改:

因为串口的状态寄存器(USART_SR) 的TXE位可以判断数据是否被转移到移位寄存器,所以加上while循环判断,等待TXE位被置1,等数据已经全部转移到移位寄存器中,才将下一个数据装入TDR寄存器中

int main()
{
	u8 temp;
	//延时初始化
	delay_Init();
	//串口初始化
	USART1_Init(9600);
	USART_SendData(USART1,'H');
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
	USART_SendData(USART1,'E');
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
	USART_SendData(USART1,'L');
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
	USART_SendData(USART1,'K');
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
	while(1)
	{		
	}
}

串口正常发送了HELK四个字符

在这里插入图片描述

接收数据

可以在主函数的while循环中接收上位机发来的数据,再将数据发送回去

接收数据是调用USART_ReceiveData函数,接收标志位是USART_FLAG_RXNE

int main()
{
	u8 temp;
	//延时初始化
	delay_Init();
	//串口初始化
	USART1_Init(9600);
	
	USART_SendData(USART1,'H');
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
	USART_SendData(USART1,'E');
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
	USART_SendData(USART1,'L');
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
	USART_SendData(USART1,'K');
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
	
	while(1)
	{
		while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET);	//等待RXNE标志位为1
		temp = USART_ReceiveData(USART1);		//接收串口1的数据
		USART_SendData(USART1,temp);			//再发送出去
		while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);		//等待发送完毕
	}
}

程序结果:

上电时单片机会发送‘HELK’四个字符,然后在串口助手上首先发送了abcd四个字符,再发送了”串口接收数据“四个汉字,串口1都接收到了,并原封不动地发送回串口助手上显示

在这里插入图片描述

发送字符串

32发送字符串的操作与51的类似

首先可以参考USART_SendData函数,写一个自己的发送字符函数,再写一个发送字符串的函数

USART_SendByte函数体内容可以直接拷贝标准库函数USART_SendData的内容,然后在最后加上等待TXE标志位的while循环即可,因为发送字符串函数需要调用到这个USART_SendByte函数,所以避免在发送字符串时前一个字符还没有转移到移位寄存器,就被后一个字符给覆盖了,造成数据丢失的情况

发送字符串原理:依次发送字符串中字符,每发送一个检查下TXE标志位,发送完全部字符以后,最后检查TC标志位。TC标志位是指数据完全从TX发送出去

/**
 * @name   USART_SendByte
 * @brief  串口发送一个字节
 * @param  Data:要发送的数据
 * @retval None
 */
void USART_SendByte(USART_TypeDef* USARTx, uint8_t Data)
{
    /* Check the parameters */
    assert_param(IS_USART_ALL_PERIPH(USARTx));
    assert_param(IS_USART_DATA(Data)); 
    
    /* Transmit Data */
    USARTx->DR = (Data & (uint16_t)0x01FF);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
}

/**
 * @name   USART_SendString
 * @brief  串口1发送一个字符串
 * @param  str:要发送的数据
 * @retval None
 */
void USART_SendString(USART_TypeDef* USARTx,char* str)
{
    while(*str != '\0')
    {
        USART_SendByte(USARTx,*str++);
    }
	while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
}

写好函数后,就可以在主函数中调用进行字符串的发送

int main()
{
	//延时初始化
	delay_Init();
	//串口初始化
	USART1_Init(9600);

	USART_SendByte(USART1,'H');
	USART_SendByte(USART1,'E');
	
	USART_SendString(USART1,"串口发送字符串\r\n");
	
	while(1)
	{
	}
}

程序结果:

每按一次复位键就发送一次’HE’字符和后面的字符串

在这里插入图片描述

中断接收数据

正因为原子的串口中断函数写有点复杂,不太好懂,但懂了之后发现其写法挺不错的,不过有时候只需要实现简单的功能,不想写这么复杂,所以这里编写一个简单的中断处理函数,将串口接收到的数据再发送回去

首先要初始化NVIC,再使能串口中断,这些都在串口初始化函数中编写

/**
 * @name   USART1_Init
 * @brief  延时初始化
 * @param  None
 * @retval None
 */
void USART1_Init(u32 bound)
{
    GPIO_InitTypeDef GPIO_InitStruc;
    USART_InitTypeDef USART_InitStruc;
    NVIC_InitTypeDef NVIC_InitStruc;
    //初始化GPIO和串口时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);

    //初始化PA9,接到TX
    GPIO_InitStruc.GPIO_Mode = GPIO_Mode_AF_PP;    //设置为推挽输出
    GPIO_InitStruc.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruc.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA,&GPIO_InitStruc);

    //初始化PA10,接到RX
    GPIO_InitStruc.GPIO_Mode = GPIO_Mode_IN_FLOATING;    //设置为浮空输入
    GPIO_InitStruc.GPIO_Pin = GPIO_Pin_10;

    GPIO_Init(GPIOA,&GPIO_InitStruc);

    //NVCI初始化
    NVIC_InitStruc.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStruc.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStruc.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStruc.NVIC_IRQChannelSubPriority = 1;

    NVIC_Init(&NVIC_InitStruc);

    //初始化串口
    USART_InitStruc.USART_BaudRate = bound;         //波特率
    USART_InitStruc.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件控制流
    USART_InitStruc.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;   //发送和接收模式
    USART_InitStruc.USART_Parity = USART_Parity_No; //无奇偶校验位
    USART_InitStruc.USART_StopBits = USART_StopBits_1;  //一位停止位
    USART_InitStruc.USART_WordLength = USART_WordLength_8b; //8位数据位

    USART_Init(USART1,&USART_InitStruc);

    //串口1使能
    USART_Cmd(USART1,ENABLE);

    //使能串口1接收中断
    USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);

}

编写中断处理函数

/**
 * @name   USART1_IRQHandler
 * @brief  中断处理函数
 * @param  None
 * @retval None
 */
void USART1_IRQHandler(void)
{
    u8 temp;
    //判断RXNE标志位是否为1,为1表示产生了接收中断
    if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
    {
        temp = USART_ReceiveData(USART1);   //对USART_DR的读操作可以将RXNE位清零
        USART_SendByte(USART1,temp);		//将接收到的数据再发送回去
    }
}

通过查看状态寄存器(USART_SR) 的RXNE标志位说明可知,只要对USART_DR的读操作就可以将RXNE位清零,所以temp = USART_ReceiveData(USART1); 这一句刚好是将DR寄存器里的值读取到 temp 变量中,所以这一句执行过后RXNE就被清零了,不用手动清零,可以进行下一次数据接收

在这里插入图片描述

最后记得在主函数中初始化NVIC分组,while循环里就不用再接收数据了

int main()
{
	//NVCI分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//设置为分组2
	//延时初始化
	delay_Init();
	//串口初始化
	USART1_Init(9600);

	USART_SendByte(USART1,'H');
	USART_SendByte(USART1,'E');
	
	USART_SendString(USART1,"串口发送字符串\r\n");
	
	while(1)
	{
		
	}
}

程序结果:
在这里插入图片描述

printf 重定向可以看这篇文章:串口通信printf函数重定向

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值