stm32——基于串口控制的双LED

总体思路
通过串口输入数据到单片机,单片机判断串口数据中的关键字,然后判定是哪一种模式(或者根据中断按键输入改变的模式)
再去对应的模式实现相应的功能

整个程序分为两个部分,程序的轮询和中断两部分

中断部分包括
定时器中断,串口中断,外部中断
定时器中断:LED2利用定时器进行延时,中断中执行相应的LED2的操作
串口中断:接收PC向单片机发送的数据。
外部中断:判断按键状态,改变MODE的值

轮询部分
数据识别:对串口中断中的数据识别,判定模式
数据输出:将可选MODE输出到串口,方便用户在PC机上查看
MODE输出:根据串口数据识别到的MODE或外部中断识别到的MODE进行相关模式的操作。

部分函数说明
void USART1_IRQHandler(void); 串口中断函数,里面定义了一个协议,所发送的字符串必须以回车键结束,很重要!!!
这个函数的数据接收过程参考https://blog.csdn.net/weixin_47586883/article/details/110959012
void usart_check(void); 这个函数的作用是对串口的数据进行识别,判定MODE
void usart_out(void); 这个函数的作用是将用户选择的MODE输出到串口
void led_out(void);这个函数的作用是根据用户选择的MODE进行相应的操作,主要是LED1,LED2的操作在相应的TIM3中断中
void TIM3_IRQHandler(void);TIM3中断函数,也包含了对相应的MODE对LED2的操作
void EXTI2_IRQHandler(void);根据外部按键,记录MODE

LED1连接A0采用systick控制时间,LED2连接A1,采用TIM3控制
外部中断按键连接A2,A2采取上拉输入,外部中断下降沿触发
A0,A1,均使用PWM输出

文件结构
在这里插入图片描述

主函数

#include"configuration.h"
 
int main(void)
 {		
 
	GPIO_Configuration();
	SysTick_Init(72);
	TIM3configuration(4999,7199);//定时时间500ms
	TIM2_CH1_CH2_PWM_Init(999,71);//1000HzPWM
	USART1_Init(9600);//波特率9600
	NVIC_Configuration();
	EXTI_Configuration();
 	while(1)
	{
		printf("\r\n可供选择的模式有:\r\n");
		printf("\r\n  MODE1:LED1亮,LED2秒闪\r\n");
		printf("\r\n  MODE2:LED1,LED2交替秒闪\r\n");
		printf("\r\n  MODE3:LED1,LED2同时秒闪\r\n");
		printf("\r\n  MODE4:LED1秒闪,LED2亮\r\n");
		printf("\r\n  MODE5:LED1,LED2为呼吸灯\r\n");
		printf("\r\n请输入选择的MODE\r\n");
		usart_check();
		usart_out();
		led_out();
	}	 
 }

#include"configuration.h"

#ifndef __CONFIGURATION_H
#define __CONFIGURATION_H
#include "stm32f10x.h"
#include "stdio.h"	

void GPIO_Configuration(void);//有关GPIO的配置和时钟配置
void NVIC_Configuration(void);//中断向量管理
void EXTI_Configuration(void);//外部中断
void USART1_Init(u32 bound);//串口初始化
void TIM3configuration(u16 per,u16 psc);//TIM3定时器初始化

void SysTick_Init(u8 SYSCLK);   //systick定时器初始化
void delay_us(u32 nus);//延时微秒
void delay_ms(u16 nms);//延时毫秒

//数据接收的相关宏
#define USART_REC_LEN  			200  	//定义最大接收字节数 200
#define EN_USART1_RX 			1		//使能(1)/禁止(0)串口1接收
	  	
extern u8  USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u16 USART_RX_STA;         		//接收状态标记寄存器	
extern uint8_t MODE;     //模式
extern uint8_t led2_value;  //读取LED2的状态

void usart_check(void);  
void usart_out(void);
void led_out(void);

void TIM2_CH1_CH2_PWM_Init(u16 per,u16 psc);  //TIM输出PWM到A0,A1


#endif

gpio_configuration

#include"configuration.h"
void GPIO_Configuration(void)
{
		GPIO_InitTypeDef GPIO_InitStructure;
	
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIOA时钟
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//复用时钟
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//TIM3时钟
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//USART1时钟
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//TIM2时钟
	
		GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_0;  //Led引脚A1,A0
		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_2;  //外部中断A2
		GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;	   
		GPIO_Init(GPIOA,&GPIO_InitStructure); 	

		GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//TX			   //串口输出PA9
		GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
		GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;	    //复用推挽输出
		GPIO_Init(GPIOA,&GPIO_InitStructure);  /* 初始化串口输入IO */
		GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//RX			 //串口输入PA10
		GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;		  //浮空输入
		GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */
}


EXTI中断

#include"configuration.h"
void EXTI_Configuration(void)
{
	EXTI_InitTypeDef EXTI_InitStructure;
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource2);//配置中断线
	EXTI_InitStructure.EXTI_Line=EXTI_Line2;//中断线PA0—PG0
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//中断方式
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;//下降沿触发
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	EXTI_Init(&EXTI_InitStructure);
}

void EXTI2_IRQHandler(void)
{
	static uint8_t EXTflag=0;
	if(EXTI_GetITStatus(EXTI_Line2)!=RESET)
	{
		delay_ms(10);
		EXTI_ClearITPendingBit(EXTI_Line2);
		EXTflag++;
		switch(EXTflag)
		{
			case 1:MODE=1;break;
			case 2:MODE=2;break;
			case 3:MODE=3;break;
			case 4:MODE=4;break;
			case 5:MODE=5;break;
		}
		if(EXTflag==5)EXTflag=0;
	}
}


NVIC_Configuration配置

#include"configuration.h"
void NVIC_Configuration(void)
{	
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//管理分组
	
	NVIC_InitStruct.NVIC_IRQChannel=TIM3_IRQn;  
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStruct);
	
	NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
	NVIC_InitStruct.NVIC_IRQChannelSubPriority =3;		//子优先级3
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStruct);
	
	NVIC_InitStruct.NVIC_IRQChannel = EXTI2_IRQn;//外部中断2通道
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级3
	NVIC_InitStruct.NVIC_IRQChannelSubPriority =3;		//子优先级3
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStruct);
}

Systick

#include"configuration.h"
static u8  fac_us=0;							//us延时倍乘数			   
static u16 fac_ms=0;							//ms延时倍乘数


//初始化延迟函数
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void SysTick_Init(u8 SYSCLK)
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 
	fac_us=SYSCLK/8;					
	fac_ms=(u16)fac_us*1000;				   
}								    


/*******************************************************************************
* 函 数 名         : delay_us
* 函数功能		   : us延时,
* 输    入         : nus:要延时的us数
					注意:nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)
* 输    出         : 无
*******************************************************************************/		    								   
void delay_us(u32 nus)
{		
	u32 temp;	    	 
	SysTick->LOAD=nus*fac_us; 					//时间加载	  		 
	SysTick->VAL=0x00;        					//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//开始倒数	  
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));		//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//关闭计数器
	SysTick->VAL =0X00;      					 //清空计数器	 
}

/*******************************************************************************
* 函 数 名         : delay_ms
* 函数功能		   : ms延时,
* 输    入         : nms:要延时的ms数
					注意:nms的值,SysTick->LOAD为24位寄存器,
					不要大于0xffffff*8*1000/SYSCLK
					对72M条件下,nms<=1864ms 
* 输    出         : 无
*******************************************************************************/
void delay_ms(u16 nms)
{	 		  	  
	u32 temp;		   
	SysTick->LOAD=(u32)nms*fac_ms;				//时间加载(SysTick->LOAD为24bit)
	SysTick->VAL =0x00;							//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//开始倒数  
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));		//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//关闭计数器
	SysTick->VAL =0X00;       					//清空计数器	  	    
} 



TIM3_init

#include "configuration.h"


void TIM3configuration(u16 per,u16 psc)
{	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	TIM_TimeBaseInitStruct.TIM_Period=per;    //定时周期
	TIM_TimeBaseInitStruct.TIM_Prescaler=psc;  //分频系数
	TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;  //时钟分频
	TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//计数模式
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);//打开更新中断
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
	TIM_Cmd(TIM3,ENABLE);
}


void TIM3_IRQHandler(void)
{
	static uint8_t TIMflag=0;
	if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET)//判断标志位状态
	{
		TIM_ClearITPendingBit(TIM3,TIM_IT_Update);//清楚中断标志位
		TIMflag++;
		switch(MODE)
		{
			case 1:
				if(TIMflag%2)
				{
					TIM_SetCompare2(TIM2,1000);
				}
				else 
				{
					TIM_SetCompare2(TIM2,0);
				}
				break;
			case 2:
				if(TIMflag%2)
				{
					TIM_SetCompare2(TIM2,1000);
					led2_value=1;
				}
				else 
				{
					TIM_SetCompare2(TIM2,0);
					led2_value=0;
				}
				break;
			case 3:
				if(TIMflag%2)
				{
					TIM_SetCompare2(TIM2,1000);
					led2_value=1;
				}
				else 
				{
					TIM_SetCompare2(TIM2,0);
					led2_value=0;
				}
				break;
		}
		if(TIMflag==200)TIMflag=0;
	}
}


usart配置

#include"configuration.h"  

#if SYSTEM_SUPPORT_OS
#include "includes.h"					//ucos 使用	  
#endif

//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 

}; 

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
_sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
	return ch;
}
#endif 

 
#if EN_USART1_RX   //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误   	
u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART_RX_STA=0;       //接收状态标记	  
  
void USART1_Init(u32 bound)
{

	USART_InitTypeDef USART_InitStructure;
	
	USART_InitStructure.USART_BaudRate = bound;//波特率设置
	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); //初始化串口1
	
	USART_Cmd(USART1, ENABLE);  //使能串口1 
	
	USART_ClearFlag(USART1, USART_FLAG_TC);//清除标志位
		
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启接收中断
}

void USART1_IRQHandler(void)                	//串口1中断服务程序
	{
	u8 Res;
#if SYSTEM_SUPPORT_OS 		//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	OSIntEnter();    
#endif
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
		{
		Res =USART_ReceiveData(USART1);	//读取接收到的数据
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
			{
			if(USART_RX_STA&0x4000)//接收到了0x0d
				{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
				}
			else //还没收到0X0D
				{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
					{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
					}		 
				}
			}   		 
     } 
#if SYSTEM_SUPPORT_OS 	//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	OSIntExit();  											 
#endif
} 
#endif	


check

#include"configuration.h"
uint8_t MODE;
void usart_check(void)  
{
	  u16 t;  
	  u16 len;	
		if(USART_RX_STA&0x8000)  
		{					   
			len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
			printf("\r\n您选择的模式是:\r\n");
			for(t=0;t<len;t++)    
			{
				USART_SendData(USART1, USART_RX_BUF[t]);         //向串口1发送数据,将输入的MODE发送到串口窗口
				while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
				if(USART_RX_BUF[t]==0x31)    //根据关键字判别模式
				{
					MODE=1;
				}
				if(USART_RX_BUF[t]==0x32)
				{
					MODE=2;
				}
				if(USART_RX_BUF[t]==0x33)
				{
					MODE=3;
				}
				if(USART_RX_BUF[t]==0x34)
				{
					MODE=4;
				}
				if(USART_RX_BUF[t]==0x35)
				{
					MODE=5;
				}
			}
			USART_RX_STA=0;  //置零串口接收状态寄存器
		}
}

void usart_out(void)   //根据识别到的模式,在串口显示
{
	switch(MODE)
	{
		case 1:
			printf("\r\n当前模式MODE1\r\n");
			break ;
		case 2:
			printf("\r\n当前模式MODE2\r\n");
			break ;
		case 3:
			printf("\r\n当前模式MODE3\r\n");
			break ;
		case 4:
			printf("\r\n当前模式MODE4\r\n");
			break ;
		case 5:
			printf("\r\n当前模式MODE5\r\n");
			break ;
		default:printf("\r\n输入MODE错误,请从新输入MODE\r\n");
	}		
}


LED_out

#include"configuration.h"
uint8_t led2_value;
void led_out(void)
{
	unsigned int i;//MODE5呼吸灯用
	switch(MODE)
	{
	    case 1:
			TIM_SetCompare1(TIM2,1000);  //A0高电平
			break;
		case 2:
			if(led2_value)
				{
					TIM_SetCompare1(TIM2,0);  //A0低电平
				}
				else
				{
					TIM_SetCompare1(TIM2,1000); 
				}
			break;
		case 3:
			if(led2_value)
				{
					TIM_SetCompare1(TIM2,1000); 
				}
				else
				{
					TIM_SetCompare1(TIM2,0); 
				}
			break;
		case 4:
			TIM_SetCompare1(TIM2,0);
			TIM_SetCompare2(TIM2,1000);//A1高电平
			delay_ms(500);
			TIM_SetCompare1(TIM2,1000); 
			delay_ms(500);
			break;
		case 5:                 //呼吸灯
			for(i=0;i<999;i++)
			{
				TIM_SetCompare2(TIM2,i);
				TIM_SetCompare1(TIM2,i);
				delay_ms(8);
			}
			for(i=999;i>0;i--)
			{
				TIM_SetCompare2(TIM2,i);
				TIM_SetCompare1(TIM2,i);
				delay_ms(8);
			}
			break;
	}
}

PWM配置

#include"configuration.h"

void TIM2_CH1_CH2_PWM_Init(u16 per,u16 psc)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_OCInitTypeDef TIM_OCInitStructure;

	TIM_TimeBaseInitStructure.TIM_Period=per;   //自动装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);	
	
	//TIM2CH2通道
	TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
	//TIM_OCInitStructure.TIM_Pulse = 800;//需要固定PWM时使用,改变PWM使用TIM_SetCompare2(TIM2,i);
	TIM_OC2Init(TIM2,&TIM_OCInitStructure); //输出比较通道2初始化
	TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Enable); //使能TIMx在 CCR2 上的预装载寄存器
	
	//TIM2CH1通道
	TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
	//TIM_OCInitStructure.TIM_Pulse = 800;//需要固定PWM时使用,改变PWM使用TIM_SetCompare1(TIM2,i);
	TIM_OC1Init(TIM2,&TIM_OCInitStructure); //输出比较通道2初始化
	TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Enable); //使能TIMx在 CCR2 上的预装载寄存器
	
	TIM_ARRPreloadConfig(TIM2,ENABLE);//使能预装载寄存器
	TIM_Cmd(TIM2,ENABLE); //使能定时器
		
}

实验效果
模式1
在这里插入图片描述
模式2
在这里插入图片描述
模式3
在这里插入图片描述
模式4
在这里插入图片描述
模式5
在这里插入图片描述
实验问题,当控制两个灯同时闪烁时,会出现时序不同步的现象,推断是使用的库函数,程序运行相对较慢,不能保持和TIM定时器一致。

  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值