USART串口发送加接收实例+代码分析

usart        串行        异步/同步        全双工

首先usart是通用同步异步收发器通过TX引脚发送RX引脚接收,

接收双方要统一的参数有波特率、数据位、校验位、停止位。

usart先发送低位再发送高位。eg:0x55对应的二进制数为01010101。发送时的顺序是1010101。低位在前高位在后。

实验内容:串口的发送加接收,PC端通过串口助手发送数据至STM32F103C8T6单片机,单片机接收到数据后回传至电脑。旋转编码器正传与反转并将数据发送至PC。

现象:

 实物图:

 

引脚连接:

 

 usb转TTL连接了USART1与USART2。

代码部分参考了B站up主江协科技。

首先配置旋转编码器模块:

旋转编码器的时序图:

配置中断:1、设置RCC       2、配置GPIO        3、配置AFIO        4、配置EXTI        5、配置NVIC

 根据时序图来配置首先打开对应的时钟我选择的是A口接单片机B0,B口接单片机B1。

EXTI中断选择下降沿触发。NVIC需要先选定优先级分组在设置抢占优先级和响应优先级。

(1条消息) STM32 NVIC_seamus的博客-CSDN博客icon-default.png?t=N658https://blog.csdn.net/DLUTXIE/article/details/7059184之后在中断来临的时候进入中断程序   

void EXTI0_IRQHandler(void)        //A

void EXTI1_IRQHandler(void)        //B

具体解释一下:根据时序图分析。在右旋旋转编码器时A、B口会产生相位相差90°的方波。当有下降沿来临触发中断进入中断程序。以右旋为例A口先产生下降沿进入中断void EXTI0_IRQHandler(void) 。检查中断标志位是否为1。为1则进一步判断B1口的电平是否为高。随后清除中断标志位。

B口后产生下降沿进入中断void EXTI1_IRQHandler(void) 。检查中断标志位是否为1。为1则进一步判断B0口的电平是否为低。随后清除中断标志位。

#include "stm32f10x.h"                  // Device header

int Encoder_Count;

void Encoder_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//打开RCC
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);		//AFIO    选择 GPIO 管脚用作外部中断线路
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);

	EXTI_InitTypeDef EXTI_InitStructure;		//
	EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;		//EXTI_Line 选择了待使能或者失能的外部线路。	
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;		//EXTI_LineCmd 用来定义选中线路的新状态
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;		//EXTI_Mode 设置了被使能线路的模式,为中断请求
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;		//EXTI_Trigger 设置了被使能线路的触发边沿,下降沿
	EXTI_Init(&EXTI_InitStructure);		//根据 EXTI_InitStruct 中指定的参数初始化外设 EXTI 寄存器

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);		//设置优先级分组:先占优先级和从优先级,2,2

	NVIC_InitTypeDef NVIC_InitStructure;		//A
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;		//该参数用以使能或者失能指定的 IRQ 通道		
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;		//该参数指定了在成员 NVIC_IRQChannel 中定义的 IRQ 通道被使能还是失能		
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		//该参数设置了成员 NVIC_IRQChannel 中的先占优先级		
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//该参数设置了成员 NVIC_IRQChannel 中的从优先级
	NVIC_Init(&NVIC_InitStructure);		//根据 NVIC_InitStruct 中指定的参数初始化外设 NVIC 寄存器

	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;		//B
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;		
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;		
	NVIC_Init(&NVIC_InitStructure);		
}

int16_t Encoder_Get(void)
{
	int16_t Temp;
	Temp = Encoder_Count;
	Encoder_Count = 0;
	return Temp;
}

void EXTI0_IRQHandler(void)		//A
{
	if (EXTI_GetITStatus(EXTI_Line0) == SET)
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
		{
			Encoder_Count --;
		}		
		EXTI_ClearITPendingBit(EXTI_Line0);
	}
}

void EXTI1_IRQHandler(void)		//B
{
	if (EXTI_GetITStatus(EXTI_Line1) == SET)
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
		{
			Encoder_Count ++;		
		}
		EXTI_ClearITPendingBit(EXTI_Line1);
	}
}

 配置        USART串口。

1、设置RCC       2、配置GPIO        3、配置USART        4、配置NVIC

接收到数据是触发中断void USART1_IRQHandler(void)        //中断程序

进入中断程序,判断中断标志位RXNE(接收中断),执行中断程序。

在采样时钟的最后一个边沿,当数据被从移位寄存器传送到接收缓冲器时,设置 RXNE 标志 (
收缓冲器非空 ) ;它表示数据已经就绪,可以从 SPI_DR 寄存器读出;如果在 SPI_CR2 寄存器中
设置了 RXNEIE 位,则此时会产生一个中断;读出 SPI_DR 寄存器即可清除 RXNIE 标志位。
在一些配置中,传输最后一个数据时,可以使用 BSY 标志等待数据传输的结束。
#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

uint8_t Serial_RxData;
uint8_t Serial_RxFlag;

uint8_t Serial_RxData2;
uint8_t Serial_RxFlag2;

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);		//打开RCC
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;		//打开GPIO	TX	发送
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 ;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;		//打开GPIO	RX	接收
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 ;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;		
	USART_InitStructure.USART_BaudRate = 9600;		//设置了 USART 传输的波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;		//指定了硬件流控制模式使能还是失能
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;		//指定了使能或者失能发送和接收模式
	USART_InitStructure.USART_Parity = USART_Parity_No;		//定义了奇偶模式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;		//定义了发送的停止位数目
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//提示了在一个帧中传输或者接收到的数据位数
	USART_Init(USART1, &USART_InitStructure);		//根据 USART_InitStruct 中指定的参数初始化外设 USARTx 寄存器
		
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);		//使能或者失能指定的 USART 中断	USART_IT_RXNE 接收中断
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);		//设置优先级分组:先占优先级和从优先级,2,2
	
	NVIC_InitTypeDef NVIC_InitStructure;		
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;		//该参数用以使能或者失能指定的 IRQ 通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;		//该参数指定了在成员 NVIC_IRQChannel 中定义的 IRQ 通道被使能还是失能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;		//该参数设置了成员 NVIC_IRQChannel 中的先占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//该参数设置了成员 NVIC_IRQChannel 中的从优先级
	NVIC_Init(&NVIC_InitStructure);		//根据 NVIC_InitStruct 中指定的参数初始化外设 NVIC 寄存器
	
	USART_Cmd(USART1, ENABLE);		//使能或者失能 USART 外设
}

void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);		//通过外设 USARTx 发送单个数据
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);		//检查指定的 USART 标志位设置与否
}

void Serial_SendByte2(uint8_t Byte)
{
	USART_SendData(USART2, Byte);		//通过外设 USARTx 发送单个数据
	while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);		//检查指定的 USART 标志位设置与否
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);
	}
}

void Serial_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		Serial_SendByte(String[i]);
	}
}

uint32_t Serial_Pow(uint32_t X, uint32_t Y)		//次方函数
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}

void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
	}
}

int fputc(int ch, FILE *f)		//printf的底层文件
{
	Serial_SendByte(ch);
	return ch;
}

//多个printf的输出
void Serial_Printf(char *format, ...)		//封装sprint
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_SendString(String);
}

uint8_t Serial_GetRxFlag(void)
{
	if (Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}

uint8_t Serial_GetRxData(void)
{
	return Serial_RxData;
}

void USART1_IRQHandler(void)		//中断程序
{
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		Serial_RxData = USART_ReceiveData(USART1);		//返回 USARTx 最近接收到的数据
		Serial_RxFlag = 1;
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}

serial.h

#ifndef __SERIAL_H
#define __SERIAL_H

#include <stdio.h>

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);

uint8_t Serial_GetRxFlag(void);
uint8_t Serial_GetRxData(void);

#endif

 

主函数 

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "Encoder.h"

uint8_t RxData;
uint8_t RxData2;
int16_t Num;
int16_t Num1;
int main(void)
{
	OLED_Init();
	Encoder_Init();
	
	OLED_ShowString(1, 1, "RxData:");
	
	Serial_Init();
	Serial_SendByte('A');
	Serial_SendString("\r\n");
	uint8_t myarray[] = {'b',0x43,0x44,0x45};
	Serial_SendArray(myarray, 4);
	Serial_SendString("\r\n");
	Serial_SendString("hello word!\r\n");
	
	Serial_SendNumber(12345,5);
	
	printf("\r\nnum=%d\r\n",666);
	
	char string[100];		//更全面的print
	sprintf(string,"num=%d\r\n",667);
	Serial_SendString(string);
	
	Serial_Printf("num=%d\r\n",668);
	
	Serial_Printf("你好,世界\r\n");
	OLED_ShowString(3, 1, "Num:");
	
	
	while (1)
	{
		if (Serial_GetRxFlag() == 1)
		{
			RxData = Serial_GetRxData();
			Serial_SendByte(RxData);
			OLED_ShowHexNum(1, 8, RxData, 2);
		}
		
		Num += Encoder_Get();
		OLED_ShowSignedNum(3, 5, Num, 5);
		if (Num1 != Num)
		{
			Num1 = Num;
			Serial_Printf("num=%d\r\n",Num);
		}
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值