STM32开发,定时器和状态机实现不一样的跑马灯

1 概述

1.1 资源概述

开发板:正点原子STM32F103 Nano开发板
CUBEMX版本:1.3.0
MDK版本:5.23
主控芯片型号:STM32F103RBT6
正点原子开发板

1.2 代码移植

移植armfly安富莱的代码,代码名称为《V4-001_不一样的流水灯(软件定时器、状态机)(V1.0)》,开发板的主控芯片为STM32F103ZE,均属于M3内核芯片,但是ZE的外设资源多很多,总共144个引脚。但是很可惜,他们的开发板基本没有视频教程,不太方便新人学习。但是代码写的是真的好。非常规范和工整。选择这个程序进行移植时由于都是103芯片,时钟相同,外部晶振也是相同的。程序也相对比较简单。
安富莱开发板

1.3 实现功能

这个流水灯程序实现功能如下:
(1)上电时,LED1点亮,闪烁3次,闪烁频率为精确的1Hz。 — 状态0 (持续3秒)。
(2)依次点亮4个LED中的一个, 实现流水灯的效果。— 状态1 (持续5秒)。
(3)依次熄灭4个LED中的一个,实现第2种流水灯效果。 — 状态2 (持续5秒)。
(4)(状态0)–>(状态1)–>(状态2 ))–>(状态1)–>(状态2 )…
这个例子应用1个systick中断实现几个软件定时器,用来控制LED指示灯的闪烁时间。主程序采用了状态机编程方法。

2 软件实现

2.1工程修改

1,修改.s启动文件为startup_stm32f10x_md.s。
修改S文件
2,修改器件为STM32F103RB。
修改器件
3,确认内存容量,当从大容量芯片变更为小容量芯片时,如果程序很大,则需要精简程序。
修改内存容量
4,修改Define,这里为MD,而不是HD,这里时全局宏定义。
修改define
5,修改下载器类型,这里选择STLINK
下载器类型
6,进入下载器设置,确认是否是中等容量flash
确认容量
7,修改代码外设,不仅仅时GPIO,还有定时器,通讯等,都要改为和目标板对应。如果时跨平台还要修改时钟等。安富莱对应LED灯部分原理图。
LED原理图
安富莱对应程序代码,安富莱将硬件驱动整合成bsp文件,并在bsp文件下细分硬件分支。
LED外设
按照Nano开发板,改为如下
GPIO修改
8,删除多余外设资源。STM32F103RB只有四个定时器,没有TIM5,删除。
删除TIM5
9,编译下载,建议改代码时,改一块编译一次,否则错误太多不好查找原因更改。
编译下载

2.2 main函数代码

代码非常规整,注释也很到位,非常值得我们学习。在后续研究中,我依据相关的状态机知识,重新改写了状态机部分的函数,定了了状态和事件两个枚举变量,并定义了对应的二维数组,使事件和状态对应起来。另外原来的代码,使用BEEP作为错误处理函数,但是在初始化中并未对BEEP进行声明,因此增加了BEEP的相关初始化,并在第一个状态中,将BEEP的状态与LED1的状态保持相同,实现相同的翻转功能。状态机的状态切换有点类似于RTOS系统中的任务切换,在此代码中,也采用了类似RTOS的时间片的概念,当事件到达时,退出当前的状态,切换到下一个状态。

#include "bsp.h"				/* 底层硬件驱动 */

/* 定义例程名和例程发布日期 */
#define EXAMPLE_NAME	"V4-001_不一样的跑马灯(软件定时器、状态机)"
#define EXAMPLE_DATE	"2015-08-30"
#define DEMO_VER		"1.0"

static void status_0(void);
static void status_1(void);
static void status_2(void);

#define MAX_STATUS 3
#define MAX_EVENT 3

enum status_led//定义状态
{
status0 = 0,
status1,
status2
};

enum event_led//定义事件
{
switch0 = 0,
switch1,
switch2
};

int led_flow[MAX_STATUS][MAX_EVENT] =   //状态机的二维数组,当前事件+触发事件=下一个状态
{
[status0][switch0] = status1,
[status1][switch1] = status2,
[status2][switch2] = status1,
};

int led_next_status(int status,int switchled)  //转态转移函数
{
return led_flow[status][switchled];
}

int main(void)
{
	
	int next_status = status0; //初始 状态
	int event = switch0;	/* 初始事件 */	

	bsp_Init();		/* 硬件初始化 */
	
	/* 状态机大循环 */
	while (1)
	{
		switch (next_status)
		{
			case status0:			/* 上电执行一次。LED1闪烁3次,每次间隔1秒。3次后状态机返回。*/
				status_0();	
				event = switch0;	/* 触发新的事件0 */
				break;
			
			case status1:			/* LED1 - LED8 依次流水显示。每次点亮1个LED。状态持续5秒后返回。 */
				status_1();		
				event = switch1;	/* 触发新的事件1 */
				break;
			
			case status2:
				status_2();	/* LED1 - LED8 依次流水显示。每次点亮3个LED, 熄灭1个。状态持续5秒后返回。*/
				event = switch2;	/* 触发新的事件2 */
				break;
		}	
		next_status = led_next_status(next_status,event);//状态转移
	}
}

/*
*********************************************************************************************************
*	函 数 名: status_0
*	功能说明: 状态0  上电执行一次。LED1闪烁3次,每次间隔1秒。3次后状态机返回。
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
static void status_0(void)
{
	/* 关闭LED */
	bsp_LedOff(1);
	bsp_LedOff(2);
	bsp_LedOff(3);
	bsp_LedOff(4);
	bsp_LedOff(5);
	bsp_LedOff(6);
	bsp_LedOff(7);
	bsp_LedOff(8);	
	/* 点亮 LED1 */
	bsp_LedOn(1);
	bsp_BeepOn();
	
	bsp_StartTimer(0, 3000);		/* 定时器0是3000ms 单次定时器 */		
	bsp_StartAutoTimer(1, 500);		/* 定时器1是500ms 自动重装定时器, 控制LED1按1Hz频率翻转闪烁 */
	while (1)
	{			
		bsp_Idle();		/* CPU空闲时执行的函数,在 bsp.c */
		
		/* 这个地方可以插入其他任务 */		
		
		/* bsp_CheckTimer()检查定时器1时间是否到。函数形参表示软件定时器的ID, 值域0 - 3 */
		if (bsp_CheckTimer(1))		
		{
			bsp_LedToggle(1);		/* 间隔500ms 翻转一次 LED1 */
			bsp_BeepToggle();		/* 间隔500ms 响一次BEEP */	
		}
		
		/* 检查定时器0时间是否到 */
		if (bsp_CheckTimer(0))
		{
			/* 3秒定时到后退出本状态 */
			break;
		}
	}
	
	/* 任务结束时,应该关闭定时器,因为他们会占用后台的资源 */
	//bsp_StopTimer(0);	 单次定时器如果超时到过一次后,可以不必关闭
	bsp_StopTimer(1);
}

/*
*********************************************************************************************************
*	函 数 名: status_1
*	功能说明: 状态1。 LED1 - LED4 依次流水显示。每次点亮1个LED。状态持续5秒后返回。
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
static void status_1(void)
{
	uint8_t led_no = 1;		/* LED指示灯序号 1-4 */
	
	bsp_StartTimer(0, 5000);		/* 定时器0是5000ms 单次定时器 */
	bsp_StartAutoTimer(1, 200);		/* 定时器1是500ms 自动重装定时器, 控制LED1按1Hz频率翻转闪烁 */
	bsp_LedOn(1);
	led_no = 1;
	while (1)
	{			
		bsp_Idle();		/* CPU空闲时执行的函数,在 bsp.c */
		
		/* 这个地方可以插入其他任务 */		
		
		/* 检查定时器0时间是否到 */
		if (bsp_CheckTimer(0))
		{
			break;
		}

		if (bsp_CheckTimer(1))		/* 检查自动定时器2,间隔200ms翻转一次LED1 */
		{
			/* 先关闭所有的LED,然后在打开其中一个 */
			bsp_LedOff(1);
			bsp_LedOff(2);
			bsp_LedOff(3);
			bsp_LedOff(4);
			bsp_LedOff(5);
			bsp_LedOff(6);
			bsp_LedOff(7);
			bsp_LedOff(8);
			bsp_BeepOff();			
			if (++led_no == 9)
			{
				led_no = 1;
			}

			bsp_LedOn(led_no);	/* 点亮其中一个LED */	
		}		
	}
	
	/* 任务结束时,应该关闭定时器,因为他们会占用后台的资源 */
	//bsp_StopTimer(0);	 单次定时器如果超时到过一次后,可以不必关闭
	bsp_StopTimer(1);
}

/*
*********************************************************************************************************
*	函 数 名: status_2
*	功能说明: 状态2.  LED1 - LED4 依次流水显示。每次点亮3个LED, 熄灭1个。状态持续5秒后返回。
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
static void status_2(void)
{
	uint8_t led_no = 1;		/* LED指示灯序号 1-4 */
	
	bsp_StartTimer(0, 5000);		/* 定时器0是5000ms 单次定时器 */
	bsp_StartAutoTimer(1, 200);		/* 定时器1是500ms 自动重装定时器, 控制LED1按1Hz频率翻转闪烁 */
	bsp_LedOn(1);
	led_no = 1;
	while (1)
	{			
		bsp_Idle();		/* CPU空闲时执行的函数,在 bsp.c */
		
		/* 这个地方可以插入其他任务 */		
		
		/* 检查定时器0时间是否到 */
		if (bsp_CheckTimer(0))
		{
			break;
		}

		if (bsp_CheckTimer(1))		/* 检查自动定时器2,间隔200ms翻转一次LED1 */
		{
			/* 先打开所有的LED,然后在关闭其中一个 */
			bsp_LedOn(1);
			bsp_LedOn(2);
			bsp_LedOn(3);
			bsp_LedOn(4);		
			bsp_LedOn(5);
			bsp_LedOn(6);
			bsp_LedOn(7);
			bsp_LedOn(8);		
			bsp_BeepOff();			
			if (++led_no == 9)
			{
				led_no = 1;
			}

			bsp_LedOff(led_no);	/* 点亮其中一个LED */			
		}		
	}
	
	/* 任务结束时,应该关闭定时器,因为他们会占用后台的资源 */
	//bsp_StopTimer(0);	 单次定时器如果超时过一次后,可以不必执行stop函数
	bsp_StopTimer(1);
}

/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/

3 实验结果

下载完程序之后,复位运行程序。观察开发板上的LED1-LED4 指示灯的状态。与设计预期一致。后续增加8个灯后,实现了8个灯的流水灯。
在这里插入图片描述

4 程序地址

程序源代码如下,仅供学习用。
https://gitee.com/whaishen_whaishen/FSMLED.git

  • 2
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
实现跑马灯程序,可以使用 STM32 的通用定时器来控制 LED 灯的闪烁频率。下面是一个简单的跑马灯程序的实现步骤: 1. 首先,需要初始化 GPIO 端口,将要控制的 LED 灯连接到 STM32 开发板的 GPIO 引脚。 2. 然后,需要初始化定时器 TIM2。可以选择使用 TIM2 或者其他的可用定时器。在初始化定时器时,需要设置计数模式、分频系数、计数周期等参数。 3. 接下来,需要配置定时器的中断,使得每次计数完成时,定时器会触发中断。 4. 在中断处理函数中,可以将 LED 灯的状态取反,从而实现 LED 灯的闪烁效果。 下面是一个完整的示例代码: ```c #include "stm32f10x.h" void GPIO_Configuration(void); void TIM2_Configuration(void); int main(void) { GPIO_Configuration(); TIM2_Configuration(); while(1); } void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_SetBits(GPIOB, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3); } void TIM2_Configuration(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseStructure.TIM_Period = 999; TIM_TimeBaseStructure.TIM_Prescaler = 7199; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { GPIO_ToggleBits(GPIOB, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3); TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } ``` 在上述代码中,我们使用了 TIM2 定时器,并将其分频系数设置为 7199,计数周期设置为 999。这意味着定时器将以 10ms 的频率触发中断,从而实现 LED 灯的闪烁效果。 在 GPIO 配置函数中,我们将 LED 灯连接到了 GPIOB 的引脚 0、1、2、3 上,并将它们初始化为输出模式,初始化为高电平。在中断处理函数中,使用 GPIO_ToggleBits 函数将 LED 灯的状态取反。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值