目录
摘要
跑马灯实验通过控制LED灯的开关,帮助学生理解如何与硬件进行交互。这涉及到GPIO(通用输入输出端口)的配置和操作,是嵌入式系统基本功能之一。跑马灯实验虽然简单,但涵盖了嵌入式系统开发的重要基础知识和技能。通过这一实验,学生能够建立起对嵌入式系统的基本理解,并为后续更复杂的项目打下坚实的基础。
1.准备工作
1.1硬件准备
野火STM32开发板:基于STMicroelectronics的STM32系列微控制器,如STM32F1、STM32F4等。这些开发板提供了丰富的外设和接口,适用于各种嵌入式应用开发。
LED灯:用于显示跑马灯效果的灯具。选择合适的颜色和尺寸。
电阻:用于限制LED的电流,防止损坏。
面包板:用于连接电路和原型测试。
杜邦线:用于连接野火stm32开发板、LED和电阻等元件。
1.2软件准备
官方C51版本下载地址:https://www.keil.com/demo/eval/c51.htm
官方ARM版本下载地址:https://www.keil.com/demo/eval/arm.htm
USB数据线:用于连接开发板和计算机,以便上传代码。
驱动程序:有些克隆板可能需要安装额外的驱动程序才能与计算机通信。
基础电子知识:了解LED的接线方法、电阻的作用等基础电子知识,有助于正确连接硬件。
2.创建项目
首先,创建一个新的Keil项目并命名为"LED_Blink"(项目名称可自行命名)。然后按照以下步骤进行设计。
2.1添加源文件
在Project窗口上右键单击源组文件夹,选择"Add New Item to Group 'Source Group'"。
2.2建立头文件
创建一个新的C源文件并命名为"main.c"。
2.2.1把头文件*.h的位置在系统中指定出来
2.2.2在main函数的文件导入头文件
添加头文件完成后即可在头文件中编写代码。
3.编写代码
3.1编写代码注意事项
确认LED连接正确:在编写代码之前,确保你正确连接了LED灯到野火开发板的引脚。如果LED连接错误,代码将无法正确控制LED的闪烁。
选择合适的引脚:在定义LED引脚时,选择未被其他组件使用的引脚,并确保这些引脚支持输出功能。
适当设置延时时间:在控制LED闪烁时,适当的延时时间可以影响闪烁效果。延时时间太短会导致LED闪烁过快,而延时时间太长则会造成闪烁效果不明显。
避免阻塞代码:在使用delay()
函数时,注意这会阻塞代码的执行,导致其他任务无法同时进行。如果需要同时执行其他任务,可以考虑使用非阻塞延时的方法,如使用millis()
函数结合时间戳来控制LED的闪烁。
合理使用循环:在控制LED灯依次闪烁时,确保循环的范围和逻辑正确,避免出现越界访问或逻辑错误导致的问题。
正确设置引脚模式:在setup()
函数中,确保正确设置LED引脚为输出模式,以使其可以控制LED灯的亮灭。
调试和测试:在编写完代码后,通过调试和测试确保代码的正确性和LED灯的正常闪烁效果。可以通过串口输出调试信息或者添加额外的指示灯来帮助调试。遵循这些注意事项可以帮助你编写出稳定运行且具有良好跑马灯效果的代码。
编写程序控制LED灯实现跑马灯效果,下面是一个简单的示例代码(仅供参考):
#include "stm32f4xx.h"
#include "gpio.h"
uint8_t a;
uint8_t flag;
uint16_t rx; //接收变量
int main (void) //主函数,所有的编译都是从主函数开始的
{
gpioSetup();//调用函数
NVIC_Setup();
EXTI_Setup();
usart_setup();
LED_1_OFF;
LED_2_OFF;
LED_3_OFF;
while(1)
{
if(flag==0){
LED_1_ON;
LED_2_OFF;
LED_3_OFF;
delay_ms(5000);
LED_1_OFF;
delay_ms(5000);
LED_3_ON;
delay_ms(5000);
LED_3_OFF;
delay_ms(5000);
LED_2_ON;
delay_ms(5000);
LED_2_OFF;
delay_ms(8000);
USART_SendData( USART1,'A');
Sendstring("中国馆");
if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET) //扫描法获取状态信息
{
rx= USART_ReceiveData(USART1);
USART_SendData(USART1,rx);
if(rx==0x11)
{
LED_1_OFF;
}
if(rx==0x12)
{
LED_1_ON;
}
if(rx==0xFF)
{
LED_1_ON;
}
if(rx==0x13)
{
LED_2_OFF;
}
if(rx==0x14)
{
LED_2_ON;
}
if(rx==0x15)
{
LED_3_OFF;
}
if(rx==0x16)
{
LED_3_ON;
}
}
}
}
gpio.c中的代码如下(仅供参考)
#include "stm32f4xx.h"
void gpioSetup(void)//1、管脚初始化,函数定义
{
//2 创建结构体对象
GPIO_InitTypeDef GPIO_InitStruct;//结构体是把一组相关的信息集成在一个变量里面【GPIO_InitTypeDef结构体创建一个结构对象】
//3 打开管脚时钟 rcc[时钟管理](如果同时打开GPIOA 和GPIOH 和GPIOC时钟)
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOH|RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOC , ENABLE);
//4 管脚模式设置(方向、模式、上下拉方式、速度)
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT ; //输出模式
GPIO_InitStruct .GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12 ; // GPIO_InitStruct.GPIO_Pin= GPIO_Pin_10;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP ; //上下拉
GPIO_InitStruct.GPIO_Speed = GPIO_High_Speed ; //高速
//1.先调用初始化库函数 init
GPIO_Init( GPIOH, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN ; //输入模式
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 ; // GPIO_InitStruct.GPIO_Pin= GPIO_Pin_10;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不拉
GPIO_Init( GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13 ; // GPIO_InitStruct.GPIO_Pin= GPIO_Pin_10;
GPIO_Init( GPIOC, &GPIO_InitStruct);
}
void delay_ms(int a)
{
a=a*1000;
while(a--);
}
void NVIC_Setup(void) //中断嵌套分组及优先级设置
{
NVIC_InitTypeDef NVIC_InitStruct; //创建结构体对象
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStruct.NVIC_IRQChannel=EXTI0_IRQn; //设置PA0中断请求通道
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1; //设置中断主优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority=1; //设置中断子优先级
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; //中断请求开关
NVIC_Init(&NVIC_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel=EXTI15_10_IRQn; //设置PC13中断请求通道
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1; //设置中断主优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority=1; //设置中断子优先级
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; //中断请求开关
NVIC_Init(&NVIC_InitStruct);
}
void EXTI_Setup(void) //中断初始化设置
{
EXTI_InitTypeDef EXTI_InitStruct; //创建对象变量
RCC_APB2PeriphClockCmd( RCC_APB2Periph_SYSCFG,ENABLE); //开系统时钟
NVIC_Setup(); //调用NVTIC
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource13);
EXTI_InitStruct.EXTI_Line= EXTI_Line0|EXTI_Line13 ;//中断线
EXTI_InitStruct.EXTI_Mode= EXTI_Mode_Interrupt; //中断模式
EXTI_InitStruct.EXTI_Trigger= EXTI_Trigger_Rising; //触法方式
EXTI_InitStruct.EXTI_LineCmd= ENABLE; //中断线开关
EXTI_Init(&EXTI_InitStruct);
}
void usart_setup(void) //用串口1,PA9=RX,PA10=TX
{
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF ;
GPIO_InitStruct.GPIO_OType= GPIO_OType_PP;
GPIO_InitStruct.GPIO_Pin= GPIO_Pin_9;
GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_UP ;
GPIO_InitStruct.GPIO_Speed= GPIO_Fast_Speed;
GPIO_Init( GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF ;
GPIO_InitStruct.GPIO_Pin= GPIO_Pin_10;
GPIO_Init( GPIOA, &GPIO_InitStruct);
USART_InitStruct.USART_BaudRate=19200; //波特率,负责通信的快慢,常用数据9600,19200
USART_InitStruct.USART_HardwareFlowControl= USART_HardwareFlowControl_None;//硬件流控制,关闭
USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; //工作模式:发送,接收
USART_InitStruct.USART_Parity= USART_Parity_No ; //奇偶校验位,关闭
USART_InitStruct.USART_StopBits= USART_StopBits_1; //停止位,1位
USART_InitStruct.USART_WordLength= USART_WordLength_8b;//每一组发送字位宽度,常用8bit
USART_Init( USART1,&USART_InitStruct);
// USART_ITConfig(USART1,USART_IT_RXNE, ENABLE);
USART_Cmd( USART1, ENABLE);
}
//中断服务函数设计,就是当发生中断后,要处理哪些问题,先设计按键1的中断
//每个中断服务函数的名字是系统自带的,已经明确不能修改
void Sendstring (char* st) //st在这里定义为指针类型,存储的是char数据类型
{
while(*st!='\0')
{
USART_SendData(USART1, *st); //此处st是指针,是地址信息,在指针前添加*号表示该指针对应的内容
while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET);
st++;
}
}
3.1编译和烧录
将代码编译并烧录到开发板中。
3.1.1编译注意事项
选择正确的开发板和端口:在keil5中,确保选择了正确的开发板型号和通信端口,以便正确编译代码。
检查代码错误:在编译之前,确保代码中没有语法错误或逻辑错误,以避免编译失败。
库文件依赖:如果代码中使用了库文件,确保这些库文件已正确安装并在代码中引用。
3.1.2烧录注意事项
选择正确的烧录器件:在keil5中,选择正确的烧录器件以与野火开发板兼容。
烧录模式:确保野火开发板处于烧录模式,通常需要按下相应的按钮或跳线,以便与计算机通信。
烧录过程监控:在烧录过程中,密切关注IDE输出窗口中的信息,确保烧录过程顺利进行。
烧录完成提示:一旦烧录完成,keil5会给出相应的提示,确认烧录成功后即可断开连接并测试项目功能。
4.运行效果
运行程序后,LED灯会按照代码中设定的时间间隔依次闪烁,形成跑马灯效果。
4.1显示效果描述
循环闪烁:LED灯按照一定的顺序依次亮起和熄灭,形成一种循环闪烁的效果。
流动效果:LED灯从一个端点开始依次点亮,然后逐渐在各个LED之间流动,直到最后一个LED点亮后重新开始。
速度调节:可以通过调整延时时间来控制LED灯闪烁的速度,从而改变跑马灯的流动效果。
多种颜色:如果使用不同颜色的LED灯,可以实现彩色跑马灯效果,使整个效果更加生动多彩。
反向效果:LED灯的流动方向可以根据需要正向或反向,以及循环或单向。
总的来说,跑马灯的显示效果是一种动态的、流动的光效,通过LED灯的亮灭变化展现出来。这种效果常用于展示、装饰、提示等场合,具有一定的视觉吸引力和装饰效果。在制作跑马灯时,可以根据自己的需求和创意来调整LED灯的亮灭顺序、速度和颜色,以实现不同的显示效果。
5.调试和优化
5.1调试跑马灯效果
检查硬件连接:确保LED正确连接到Arduino引脚,并且每个LED都连接了正确的电阻。
检查引脚设置:确认在代码中正确设置了每个LED的引脚号。
检查代码逻辑:检查代码中循环和延时部分的逻辑是否正确,确保LED按照预期的顺序亮灭。
串口输出:在代码中添加串口输出语句,输出调试信息,以便查看程序运行时的状态和问题。
逐步调试:可以逐步调试代码,逐个确认每个LED的亮灭情况,以定位问题所在。
5.2优化跑马灯效果
调整延时时间:通过调整延时时间可以改变LED闪烁的速度,使跑马灯效果更流畅或更快速。
改变亮灭顺序:尝试改变LED的亮灭顺序,可以创建不同的跑马灯效果,如反向跑马灯或随机跑马灯。
增加颜色效果:如果使用多种颜色的LED,可以尝试在跑马灯效果中切换不同颜色,增加视觉效果。
添加效果变化:在跑马灯效果中加入渐变、闪烁或跳动等变化效果,使整体效果更加生动。
优化代码:优化代码结构和逻辑,消除不必要的重复代码,提高代码的可读性和效率。
6.总结
通过不断调试和优化,你可以实现更加理想的跑马灯效果,使其更具有吸引力和视觉效果。记得在调试过程中保持耐心,一步一步地解决问题,同时尝试发挥创意,定制出符合自己需求的独特跑马灯效果,这个过程可以帮助你熟悉嵌入式系统的开发流程,加深对硬件和软件的理解,同时也可以为你后续更复杂的项目打下基础。