实验1、流水灯
一、实验目的
熟悉keil开发环境和实验板的使用,了解并熟悉实验板上单片机I/O口和LED灯的电路结构,掌握STM32单片机I/O口编程方法,掌握顺序控制程序的简单编程。
二、实验内容
1、实验原理
I/O口是单片机与外界联系的通道,它可对各类外部信号(模拟量、开关量、频率信号)进行检测、判断和处理,并可控制各类外部设备。单片机通过I/O与外部世界互相感知。
图示电路中,有L0~L7共八个发光二极管,当引脚LED_SEL输入为1,对于A~H引脚,只要输入为1,则点亮相连接的发光二极管。
A~H引脚连接STM32F108VB芯片的PE8~PE15,程序初始化时,对其进行初始设置。引脚LED_SEL为1时,发光二极管才工作,否则右边的数码管工作。
注意,LED_SEL连接于PB3,该引脚具有复用功能,在默认状态下,该引脚的IO不可用,需对AFIO_MAPR寄存器进行设置,设置其为IO可用。
2、实验步骤
(1)创建keil项目
启动keil,选择Project | new uvision Project,输入项目名称“led”,系统自动追加后缀为“.uvproj”。选择存储的文件夹为d:\xxx\led(建议所有文件名都使用英文字母开头),选择芯片为STMicroelectronics公司的STM32F103VB。
(2)编写文件代码
点击“File | new…”,生成一个空白文件,选择“File | save”保存为d:\xxx\led\led.c。后面所有的.c文件都如此操作。这是一个存储与硬件相关函数的文件,其中包含了二极管与数码管的初始化和实现;再用同样方法建立与led.c相关的led.h、delay.c、led.c、sys.c以及相应的.h文件;最后建立main.c并保存。
(3)将文件载入工程
按“+”展开project窗口中的Target,出现Source Group,在右键弹出的菜单中选择“Add Files to Group ‘Source Group’”,控制文件对话框进入d:\xxx\led,选择stm32f10x.s,将stm32f10x.s这个启动文件以及led.c、delay.c、sys.c、usart.c、main.c依次加入到项目中去。
(4)编译调试
单击按钮栏“Build”或“Rebuild”,对整个工程进行编译连接,观察文件夹下增加了哪些文件;如果出现了错误,则在下方的Command窗口中按照错误信息进行分析;之后可在按钮栏“Start/Stop debug session”进行调试。
(5)烧录运行
启动MCUISP并连接STM32单片机,数据线分别连接STM32单片机上的COM口和计算机上的USB口,先搜索串口并选择对应的COM口,再选择“联机下载时的程序文件”为对应项目生成的.hex文件,最后点击“开始编程”,并先后快速且连续地按下“RESET”按键和“ISP”按键。
(6)记录实验结果并分析
观察烧录启动后实验板上的现象并记录。
三、实验结果与分析
1、实验结果
观察到实验板上L0、L1、L2、L3、L4、L5、L6、L7发光二极管从右至左依次点亮,间隔300ms。当L0、L1、L2、L3、L4、L5、L6、L7发光二极管全部点亮后,L0、L1、L2、L3、L4、L5、L6、L7发光二极管同时熄灭,间隔300ms后,L0、L1、L2、L3、L4、L5、L6、L7发光二极管再次从右至左依次点亮。如此反复循环。
2、实验分析
(1)引入头文件
#include "sys.h"
#include "delay.h"
#include "led.h"
这里引入了三个头文件:
① sys.h头文件包含了STM32单片机的一些基本宏定义;
② delay.h头文件包含了延时函数的定义;
③ led.h头文件包含了LED灯控制的函数定义。
(2)系统初始化
Stm32_Clock_Init( 6 );
delay_init( 72 );
LED_Init();
这里进行了系统初始化,包括时钟初始化、延时函数初始化和LED灯初始化。
(3)GPIO 初始化
GPIOE->ODR &= ~(0xff<<8);
LED_SEL = 1;
这里使用了STM32单片机中的GPIOE引脚来控制LED灯的亮灭,通过设置ODR寄存器的值来实现。LED_SEL是一个宏定义,用于选择需要控制的LED灯。
(4)流水灯控制
light = 0x01;
while( 1 )
{
GPIOE->ODR |= (light<<8);
delay_ms( 300 );
light = light<<1;
if( light==0x00 )
{
GPIOE->ODR &= ~(0xff<<8);
delay_ms( 300 );
light = 0x01;
}
}
这里使用了while(1)循环来不断重复控制LED灯的亮灭,实现流水灯的效果。首先将light 变量赋值为0x01,然后在循环中不断左移,即将灯光从右往左移动。当light变量左移8次(即灯光移到最左边)时,将ODR寄存器的值清零,然后重新将light变量赋值为0x01,即将灯光从左往右移动。
实验2、8位数码管动态扫描
一、实验目的
了解8位数码管动态显示原理;掌握stm32f10x扩展端口的方法。
二、实验内容
1、实验原理
数码管中的A~G、DP段分别连接到电路图中的A~G、H线上,当某段上有一定的电压差值时,便会点亮该段。当E3输入为1,也就是LED_SEL输入为0时,根据SEL0~SEL2的值确定选中的数码管,即位选,再根据A~H引脚的高低电平,点亮对应段,即段选。
2、实验步骤
(1)创建keil项目
启动keil,选择Project | new uvision Project,输入项目名称“led”,系统自动追加后缀为“.uvproj”。选择存储的文件夹为d:\xxx\led(建议所有文件名都使用英文字母开头),选择芯片为STMicroelectronics公司的STM32F103VB。
(2)编写文件代码
点击“File | new…”,生成一个空白文件,选择“File | save”保存为d:\xxx\led\led.c。后面所有的.c文件都如此操作。这是一个存储与硬件相关函数的文件,其中包含了二极管与数码管的初始化和实现;再用同样方法建立与led.c相关的led.h、delay.c、led.c、sys.c以及相应的.h文件;最后建立main.c并保存。
(3)将文件载入工程
按“+”展开project窗口中的Target,出现Source Group,在右键弹出的菜单中选择“Add Files to Group ‘Source Group’”,控制文件对话框进入d:\xxx\led,选择stm32f10x.s,将stm32f10x.s这个启动文件以及led.c、delay.c、sys.c、usart.c、main.c依次加入到项目中去。
(4)编译调试
单击按钮栏“Build”或“Rebuild”,对整个工程进行编译连接,观察文件夹下增加了哪些文件;如果出现了错误,则在下方的Command窗口中按照错误信息进行分析;之后可在按钮栏“Start/Stop debug session”进行调试。
(5)烧录运行
启动MCUISP并连接STM32单片机,数据线分别连接STM32单片机上的COM口和计算机上的USB口,先搜索串口并选择对应的COM口,再选择“联机下载时的程序文件”为对应项目生成的.hex文件,最后点击“开始编程”,并先后快速且连续地按下“RESET”按键和“ISP”按键。
(6)记录实验结果并分析
观察烧录启动后实验板上的现象并记录。
(7)修改晶振频率
修改晶振频率,重新编译连接后再次进行观察,对两次结果加以比较,并分析由此产生的视觉效果差异。
三、实验结果与分析
1、实验结果
观察到实验板上8个数码管从左至右依次显示对应的数字,且每一个数码显示的数字在1-9之间循环。
修改晶振频率,可以通过加快扫描频率,达到一定的扫描频率时,八位数码管看上去像是在同时显示。
2、实验分析
(1)引入头文件
#include "sys.h"
#include "delay.h"
#include "led.h"
这里引入了三个头文件:
① sys.h头文件包含了STM32单片机的一些基本宏定义;
② delay.h头文件包含了延时函数的定义;
③ led.h头文件包含了LED灯控制的函数定义。
(2)系统初始化
Stm32_Clock_Init( 6 );
delay_init( 72 );
LED_Init();
LED_SEL = 0;
这里进行了系统初始化,包括时钟初始化、延时函数初始化和LED数码管初始化。并选择需要控制的LED数码管。
(3)数码管显示
SetLed(0, show_w1%10);
delay_ms(100);
SetLed(1, show_w2%10);
delay_ms(100);
SetLed(2, show_w3%10);
delay_ms(100);
SetLed(3, show_w4%10);
delay_ms(100);
SetLed(4, show_w5%10);
delay_ms(100);
SetLed(5, show_w6%10);
delay_ms(100);
SetLed(6, show_w7%10);
delay_ms(100);
SetLed(7, show_w8%10);
delay_ms(100);
这里使用了SetLed()函数来将计数值显示在LED数码管上。show_w1到show_w8是计数器的计数值,%10是为了保证只显示个位数。delay_ms()函数用于控制每个数码管的显示时间间隔。
(4)计数器递增
show_w1++;
show_w2++;
show_w3++;
show_w4++;
show_w5++;
show_w6++;
show_w7++;
show_w8++;
这里将计数器的计数值逐个递增,当达到最大值时会重新从0开始计数。
实验3、按键输入
一、实验目的
通过本实验了解STM32F103RB硬件结构,掌握各引脚功能和接线;了解GPIO各寄存器的功能与应用,加深对按键输入检测过程的理解。
二、实验内容
1、实验原理
本实验电路与原理与“8位数码管动态扫描”实验相同。
2、实验步骤
(1)建立Key工程,芯片选用STM32103RB,其他设置与前几个实验保持一致。拷贝参考程序清单中的代码,编译后形成可执行文件;
(2)编译链接成功后,上载到实验板,用不同的速率敲击或长按键盘按键,或者同时按下多个按键,观察LED的反转速度有什么变化;
(3)结合按键接收原理,分析上述过程;
(4)仔细阅读程序,不断修改延迟参数,找出最合适的参数设置。
三、实验结果与分析
1、实验结果
(1)敲击速率较快
LED的反转速度变得更快;
(2)敲击速率较慢
LED的反转速度变得更慢;
(3)长按键盘按键
LED只会在按键松开时反转一次,不会反复反转;
(4)同时按下多个按键
只有其中一个按键对应控制的LED反转,而其他LED不反转。
2、实验分析
按键接收原理是通过扫描检测按键的状态来实现的。在STM32单片机中,通过轮询检测按键的状态,当按键被按下时会改变对应的IO口电平状态,从而触发中断或返回按键编号,实现按键的检测和操作。
在实现按键控制LED灯的过程中,程序不断地扫描按键的状态,当检测到按键被按下时,程序通过按键编号来控制对应的LED灯反转。如果敲击速率较快,则按键状态会更快地改变,程序会更快地检测到按键的变化,从而更快地反转LED。如果敲击速率较慢,则程序需要等待一定的时间才能检测到按键的变化,从而反转LED的速度会变慢。
当按键被长按时,按键的状态不会发生变化,因此只会在按键松开时触发一次LED反转。同时按下多个按键时,程序只会检测到其中一个按键的状态变化,从而只会控制对应的LED反转,而不会对其他LED产生影响。
(1)引入头文件
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"
这里引入了四个头文件:
① sys.h头文件包含了STM32单片机的一些基本宏定义;
② delay.h头文件包含了延时函数的定义;
③ led.h头文件包含了LED灯控制的函数定义;
④ key.h头文件包含了按键控制的函数定义。
(2)系统初始化
Stm32_Clock_Init(9);
delay_init(72);
LED_Init();
KEY_Init();
这里进行了系统初始化,包括时钟初始化、延时函数初始化、LED 灯初始化和按键初始化。
(3)按键控制 LED 灯
t=KEY_Scan();
if(t)
{
switch(t)
{
case 1:
LED0=!LED0;
break;
case 2:
LED3=!LED3;
break;
case 3:
LED7=!LED7;
break;
}
}
这里使用 KEY_Scan() 函数来检测按键是否被按下,并返回按键编号。如果有按键被按下,则根据不同的按键编号控制不同的 LED 灯亮灭。LED0、LED3、LED7 分别对应不同的 LED 灯,!LEDx 表示将 LED 灯的状态取反。
(4)延时函数
else delay_ms(10);
如果没有按键被按下,则进行 10ms 的延时。
实验4、定时器-数字钟
一、实验目的
熟悉STM32的定时器和GPIO模块,了解定时器和GPIO的工作原理和使用方法;掌握使用定时器模块和GPIO模块实现数字时钟的方法;了解数字时钟的基本原理和设计思路。
二、实验内容
1、实验原理
利用STM32的定时器模块来生成时钟信号,然后通过控制GPIO模块的输出来显示时间。
2、实验步骤
(1)使用STM32的定时器模块,设置定时器的时钟源、预分频器、计数器周期等参数,从而生成一个稳定的时钟信号。
(2)使用STM32的GPIO模块,将数码管的控制引脚设置为输出模式,从而能够控制数码管的亮灭。
(3)在定时器每次计数完成后,产生一个定时器中断,中断处理函数中更新时间的变量,并将时间数据转换为数码管的显示格式。
(4)使用STM32的NVIC模块,配置定时器中断的优先级和使能中断。
(5)在主函数中启动定时器,使其开始计数,并等待定时器中断的触发。
(6)在定时器中断处理函数中,根据时间变量的更新,将时间数据转换为数码管的显示格式,并将数据输出到数码管的控制引脚上,实现数字时钟的功能。
三、实验结果与分析
1、实验结果
下载程序后,8位数码管初始化为00-00-00,分别表示时-分-秒。由定时器计时,每过1秒,“秒”的显示值加1。当“秒”的显示值加到59后,再过1s,其值变为00。同时,“分”的值加1。当“分”的值加到59,再过1分钟,“分”的值变为00。同时“时”的值加1。当“时”的值为23,再过1小时后,“时”的值变为00。
2、实验分析
(1)引入头文件
#include "timer.h"
代码中只引入了 timer.h 头文件,该头文件包含了定时器模块的函数定义。
(2)系统初始化
Stm32_Clock_Init( 6 );
delay_init( 72 );
TimerxInit( 9999,7199 );
LED_Init();
LED_SEL = 0;
这里进行了系统初始化,包括时钟初始化、延时函数初始化、定时器初始化、LED初始化。
(3)数字时钟的实时显示
while(1)
{
SetLed(0, hour/10);
delay_ms(1);
SetLed(1, hour%10);
delay_ms(1);
SetLed(2, 10);
delay_ms(1);
SetLed(3, minute/10);
delay_ms(1);
SetLed(4, minute%10);
delay_ms(1);
SetLed(5, 10);
delay_ms(1);
SetLed(6, second/10);
delay_ms(1);
SetLed(7, second%10);
delay_ms(1);
}
① 显示小时数
调用 SetLed() 函数将小时数的十位数转换为数码管的显示格式,并输出到第一个数码管的控制引脚上;再将小时数的个位数转换为数码管的显示格式,并输出到第二个数码管的控制引脚上。
② 显示冒号
调用 SetLed() 函数将冒号的显示格式输出到第三个数码管的控制引脚上。
③ 显示分钟数
调用 SetLed() 函数将分钟数的十位数转换为数码管的显示格式,并输出到第四个数码管的控制引脚上;再将分钟数的个位数转换为数码管的显示格式,并输出到第五个数码管的控制引脚上。
④ 显示冒号
调用 SetLed() 函数将冒号的显示格式输出到第六个数码管的控制引脚上。
⑤ 显示秒数
调用 SetLed() 函数将秒数的十位数转换为数码管的显示格式,并输出到第七个数码管的控制引脚上;再将秒数的个位数转换为数码管的显示格式,并输出到第八个数码管的控制引脚上。
⑥ 等待 1ms
调用 delay_ms() 函数实现 1ms 的延时,以便下一次的数码管显示。
实验5、RTC实时时钟
一、实验目的
熟悉STM32的RTC模块,了解RTC的工作原理和使用方法;掌握使用RTC模块实现实时时钟的方法;了解实时时钟的基本原理和设计思路;学习如何使用STM32的外设库函数编写实时时钟程序。
二、实验内容
1、实验原理
RTC(Real-Time Clock)实时时钟是一种能够提供精确时间信息的电子设备,通常用于计算机、移动设备、汽车电子等需要实时时间显示和计时的场合。RTC实时时钟通常由时钟芯片、晶振、电池等组成,其工作原理如下:
(1)RTC实时时钟的核心是时钟芯片,通常由数字时钟芯片、温补晶振、时钟计数器等组成。
(2)晶振是时钟芯片的重要组成部分,用于提供时钟信号。晶振的频率越高,时钟精度越高。
(3)RTC实时时钟通常需要使用电池来提供备用电源,以便在断电或者掉电的情况下仍然能够维持时钟计时的准确性。
(4)时钟计数器是时钟芯片的另一个重要组成部分,用于记录时间信息。时钟计数器通常由秒计数器、分计数器、小时计数器、日历计数器等组成。
2、实验步骤
(1)连接电路
将RTC实时时钟模块与数字电路实验板连接。通常需要连接VCC、GND、SCL、SDA等引脚,连接方式可以参考RTC实时时钟模块的说明书。
(2)编写程序
使用开发工具,编写程序实现RTC实时时钟的功能,如显示当前时间、设置时间等。程序可以使用相应的库函数,也可以手动编写程序实现。
(3)烧录程序
将编写好的程序烧录到STM32开发板中,以便实现RTC实时时钟的功能。
(4)测试程序
测试程序是否能够正常工作,如能够正确显示当前时间、能够设置时间等。如果出现问题,需要检查硬件连接或者程序代码,进行相应的修正。
(5)调试电路
如果RTC实时时钟模块不能正常工作,需要检查电路连接是否正确,电源是否稳定等。可以通过示波器等工具进行调试,以便找出问题所在。
(6)优化程序
根据实际需要,可以对程序进行优化,如加入闹钟功能、加入自动校时功能等,以提高RTC实时时钟的使用价值。
三、实验结果与分析
1、实验结果
程序下载后,有以下两种情况:
(1)芯片第一次初始化RTC,则程序将写入HEX生成的时间信息到芯片RTC时钟中,然后读出时钟信息。通过数码管显示。
(2)芯片不是第一次初始化RTC,则程序将读取RTC对应寄存器的值,以时间信息显示在数码管上。
如果开发板上有放入3V的小电池,则即使掉电,RTC时钟依然在走。程序刚下载,数码管显示的数据为时-分-秒。K1键,则显示年月日,左边四位为年份,最右边两位为日期。其它两位为月份。K2键,数码管继续显示时-分-秒。
2、实验分析
(1)引入头文件
#include "sys.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "rtc.h"
#include "usart.h"
这里引入了六个头文件: sys.h、delay.h、led.h、key.h、rtc.h 和 usart.h。
(2)定义常量
const u8 *COMPILED_DATE=__DATE__;
const u8 *COMPILED_TIME=__TIME__;
const u8* Week[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
COMPILED_DATE 和 COMPILED_TIME 分别表示代码编译的日期和时间,Week 数组表示周几的字符串。
(3)系统初始化
u8 key = 0;
Stm32_Clock_Init( 6 );
delay_init( 72 );
LED_Init();
LED_SEL = 0;
KeyInit();
RtcInit();
包括时钟初始化、延时函数初始化、LED 灯初始化、按键初始化和 RTC 模块初始化。
(4)进入主循环并进行按键检测
while(1)
{
key = KeyScan()
SetLed(0,timer.hour/10);
delay_ms(1);
SetLed(1,timer.hour%10);
delay_ms(1);
SetLed(2,10);
delay_ms(1);
SetLed(3,timer.minute/10);
delay_ms(1);
SetLed(4,timer.minute%10);
delay_ms(1);
SetLed(5,10);
delay_ms(1);
SetLed(6,timer.sec/10);
delay_ms(1);
SetLed(7,timer.sec%10);
delay_ms(1);
if( key==1 )
{
while( key==1||key==0 )
{
SetLed(0,timer.year/1000);
delay_ms(1);
SetLed(1,timer.year%1000/100);
delay_ms(1);
SetLed(2,timer.year%100/10);
delay_ms(1);
SetLed(3,timer.year%10);
delay_ms(1);
SetLed(4,timer.month/10);
delay_ms(1);
SetLed(5,timer.month%10);
delay_ms(1);
SetLed(6,timer.date/10);
delay_ms(1);
SetLed(7,timer.date%10);
delay_ms(1);
key = KeyScan();
}
}
}
不断读取 RTC 模块中的时间,并将其显示在数码管上。如果检测到按键按下,则进入设置日期和时间的模式,将当前的日期和时间显示在数码管上,并等待用户输入。如果检测到按键松开,则退出设置日期和时间的模式,并将设置好的日期和时间保存到 RTC 模块中。
实验6、定时器中断
一、实验目的
初步掌握中断编程的一般原理与基本方法;掌握定时器寄存器状态配置;进一步理解STM32系列处理器的定时器资源与设置。
二、实验内容
1、实验原理
STM32的通用定时器是一个通过可编程预分频器(PSC)驱动的16位自动装载计数器(CNT)构成。STM32的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)等。 使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32的每个通用定时器都是完全独立的,没有互相共享的任何资源。
STM3的通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器功能包括:
(1)16位向上、向下、向上/向下自动装载计数器(TIMx_CNT);
(2)16位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率分频系数为1~65535之间的任意数值;
(3)4个独立通道(TIMx_CH1~4),这些通道可以用来作为:
① 输入捕获;
② 输出比较;
③ PWM生成(边缘或中间对齐模式);
④ 单脉冲模式输出。
(4)可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用1个定时器控制另外一个定时器)的同步电路。
(5)如下事件发生时产生中断/DMA:
① 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发);
② 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数);
③ 输入捕获;
④ 输出比较;
⑤ 支持针对定位的增量(正交)编码器和霍尔传感器电路;
⑥ 触发输入作为外部时钟或者按周期的电流管理。
2、实验步骤
创建项目至烧录运行与前几个实验操作一致,观察实验现象并记录。
三、实验结果与分析
1、实验结果
TIM3的定时器中断控制L1的翻转,L0的翻转提示程序正在运行。
2、实验分析
(1)timer.c
① 引入头文件
#include "timer.h"
#include "led.h"
这里头文件引入了 timer.h 和 led.h 两个头文件,其中 timer.h 头文件包含了定时器的函数定义,led.h 头文件包含了 LED 灯控制的函数定义。
② 定时器中断处理函数
void TIM3_IRQHandler(void)
{
if(TIM3->SR&0X0001)
{
LED1=!LED1;
}
TIM3->SR&=~(1<<0);
}
这里定义了定时器 TIM3 的中断处理函数 TIM3_IRQHandler。当定时器计数器计数达到设定的值时,会触发定时器中断,执行中断处理函数。在中断处理函数中,首先判断定时器中断是否为更新中断(UIF),如果是,则将 LED1 状态取反,从而实现 LED 灯闪烁的效果。然后清除定时器中断标志位,以便下次中断的触发。
③ 定时器初始化函数
void Timerx_Init(u16 arr,u16 psc)
{
RCC->APB1ENR|=1<<1;
TIM3->ARR=arr;
TIM3->PSC=psc;
TIM3->DIER|=1<<0;
TIM3->DIER|=1<<6;
TIM3->CR1|=0x01;
MY_NVIC_Init(1,3,TIM3_IRQChannel,2);
}
这里定义了定时器初始化函数Timerx_Init,用来初始化STM32单片机的定时器TIM3。函数接受两个参数,分别是计数器的自动重装值和预分频器的分频值。首先使能定时器TIM3的时钟,然后设置计数器的自动重装值和预分频器的分频值。设置后,使能定时器更新中断和定时器溢出中断,启动定时器计数,并开启相应NVIC中断。这里设置定时器的中断优先级为2。
(2)main.c
① 引入头文件
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "exti.h"
#include "wdg.h"
#include "timer.h"
这里引入了八个头文件:
(i)sys.h头文件包含了STM32单片机的一些基本宏定义;
(ii)usart.h头文件包含了串口通信的函数定义;
(iii)delay.h头文件包含了延时函数的定义;
(iv)led.h头文件包含了LED灯控制的函数定义;
(v)key.h头文件包含了按键控制的函数定义;
(vi)exti.h头文件包含了外部中断的函数定义;
(vii)wdg.h头文件包含了看门狗定时器的函数定义;
(viii)timer.h头文件包含了定时器的函数定义。
② 系统初始化
Stm32_Clock_Init(9);
delay_init(72);
uart_init(72,9600);
LED_Init();
Timerx_Init(5000,7199);
这里进行了系统初始化,包括时钟初始化、延时函数初始化、串口通信初始化、LED 灯初始化和定时器初始化。其中,Stm32_Clock_Init() 函数用来初始化系统时钟,把系统时钟设置为 72MHz;delay_init() 函数用来初始化延时函数;uart_init() 函数用来初始化串口通信,设置波特率为 9600bps;LED_Init() 函数用来初始化 LED 灯控制的 IO 口;Timerx_Init() 函数用来初始化定时器,设置定时器的计数器和分频器的初值,这里设置定时器的时钟为 72MHz,计数器初值为 5000,分频器初值为 7199。
③ 主函数
while(1)
{
LED0=!LED0;
delay_ms(200);
}
这里进入了主函数,程序会不断地循环执行。在循环中,先将 LED0 状态取反,从而实现 LED 灯闪烁的效果。然后调用 delay_ms() 函数,延时 200ms,控制 LED 灯的亮灭时间,从而实现 LED 灯闪烁的频率。
实验7、学号移位显示
一、实验目的
了解8位数码管动态显示原理;掌握stm32f10x扩展端口的方法;掌握控制循环移位的方法。
二、实验内容
1、实验原理
嵌入式 STM32 学号移位显示实验的原理是利用STM32微控制器的GPIO口控制LED灯的亮灭来显示学号。具体步骤如下:
(1)将学号转换成二进制数
在计算机中,数字一般以二进制数的形式存储和处理。因此,需要将学号转换成二进制数,以便在STM32微控制器中进行处理和显示。将学号2005010106转换成二进制数:0010 0000 0000 0101 0000 0001 0000 0001 0000 0110。
(2)将二进制数分组
由于STM32微控制器的GPIO口数量有限,需要将二进制数分成若干组,每组包含8位二进制数。例如,将上述学号分为以下几组:0010 0000、0000 0101、0000 0001、0000 0001和0000 0110。
(3)将二进制数写入GPIO口
将每组二进制数写入到STM32微控制器的GPIO口中,其中每一位二进制数对应一个LED灯的亮灭。例如第一组二进制数0010 0000中的第一位对应第一个LED灯,第二位对应第二个LED灯,以此类推。在STM32微控制器中,每个GPIO口都有一个编号,例如PB0表示第0个GPIO口,PB1表示第1个GPIO口,以此类推。因此,可以通过设置每个GPIO口的电平来控制每个LED灯的亮灭。
(4)实现学号的移位显示
将每组二进制数依次写入GPIO口中,通过控制每个GPIO口的电平来控制每个LED灯的亮灭,从而实现学号的移位显示。例如,先将第一组二进制数0010 0000写入PA0PA7的GPIO口中,控制PA5为高电平,其他GPIO口为低电平,从而点亮第6个LED灯;然后将第二组二进制数0000 0101写入PA8PA15的GPIO口中,控制PA8、PA10、PA12、PA13、PA14为高电平,其他GPIO口为低电平,从而点亮第9、11、13、14、15个LED灯;依此类推,直到将所有组的二进制数都写入GPIO口中,同时为为学号预留等长度的不可见字符(0xff),完成学号的移位显示。
需要注意的是,STM32微控制器的GPIO口需要进行初始化配置,包括设置输入输出模式、上拉下拉电阻、时钟等等。同时,由于每个GPIO口只能控制一个LED灯的亮灭,因此需要根据需要选择合适的GPIO口进行连接,以便实现学号的移位显示。
2、实验步骤
创建项目至烧录运行与前几个实验操作一致,观察实验现象并记录。
三、实验结果与分析
1、实验结果
学号的首个数字2从数码管右端先显示,并依次左移显示出剩下的学号,直到学号全部显示并情全部退出,往复循环。
2、实验分析
(1)引用头文件
#include "sys.h"
#include "delay.h"
#include "led.h"
该部分引用了系统初始化、延时和LED控制相关的头文件。
(2)定义常量和变量
#define uchar unsigned char
int sel_led[8]={0,1,2,3,4,5,6,7};
int num[18]={9,9,9,9,9,9,9,9,2,0,0,5,0,1,0,1,0,6};
int i,j,k;
该部分定义了常量和变量,其中sel_led数组表示LED的选择,num数组表示学号的每一位数字,初始值为9999999920010106。i、j、k为循环计数器。
(3)系统初始化
Stm32_Clock_Init(6);
delay_init(72);
LED_Init();
LED_SEL = 0;
该部分对系统进行初始化,包括设置系统时钟、延时初始化和LED初始化。
(4)主程序循环
while (1) {
for (i = 0, j = 0; j < 1000; i++, j++) {
if (num[j % 8] == 9) {
LedValue(0x00);
} else {
SetLed(sel_led[i % 8], num[j % 8]);
}
delay_ms(1);
}
num[18] = num[0];
for (k = 0; k < 18; k++) {
num[k] = num[k + 1];
}
}
该部分是主程序循环,分为两个部分:
① for循环:循环1000次,每次循环控制LED显示一个数字,控制的方式是将数字转换为二进制数,然后将每一位二进制数对应的LED灯点亮或熄灭。如果当前数字为9,则将所有LED灯熄灭。
② 数组移位:将num数组的第一个元素赋值给num数组的最后一个元素,然后将num数组中每个元素的值向前移一位,以实现学号的移位显示。
需要注意的是,该代码中的LED控制函数SetLed和LedValue并未给出,需要根据具体的硬件电路进行实现。同时,为了实现学号的移位显示,需要将num数组的长度设置为18,其中前8个元素存放学号的后8位数字,后10个元素存放学号的前10位数字。
实验8、自由落体
一、实验目的
熟悉keil开发环境和实验板的使用,了解并熟悉实验板上单片机I/O口和LED灯的电路结构,掌握STM32单片机I/O口编程方法,掌握顺序控制程序的简单编程,并完成自由落体运动的运动推导。
二、实验内容
1、实验原理
I/O口是单片机与外界联系的通道,它可对各类外部信号(模拟量、开关量、频率信号)进行检测、判断和处理,并可控制各类外部设备。单片机通过I/O与外部世界互相感知。
STM32学习板中,有L0~L7共八个发光二极管,当引脚LED_SEL输入为1,对于A~H引脚,只要输入为1,则点亮相连接的发光二极管。
A~H引脚连接STM32F108VB芯片的PE8~PE15,程序初始化时,对其进行初始设置。引脚LED_SEL为1时,发光二极管才工作,否则右边的数码管工作。
注意,LED_SEL连接于PB3,该引脚具有复用功能,在默认状态下,该引脚的IO不可用,需对AFIO_MAPR寄存器进行设置,设置其为IO可用。
2、实验步骤
创建项目至烧录运行与前几个实验操作一致,观察实验现象并记录,下面给出计算距离的公式推导:
由牛顿第二定律,物体所受合力等于物体的质量乘以加速度,即
在自由落体的情况下,物体所受合力只有重力,因此有
其中m为物体的质量,g为重力加速度。由于自由落体起始时速度为零,因此可以使用初速度为0的匀加速直线运动公式
计算自由落体的距离s,其中t为下落的时间。联立牛顿第二定律和匀加速直线运动公式
其中g为重力加速度,t为下落的时间。
三、实验结果与分析
1、实验结果
假定小球的初始位置在L0,并按照公式计算从L0到L7以自由落体状态“下落”,然后在从L7到L0回弹,直至速度为0,如此往复。
2、实验分析
(1)引用头文件和定义变量
#include "sys.h"
#include "delay.h"
#include "led.h"
u8 light;
该部分引用了系统初始化、延时和LED控制相关的头文件,并定义了一个变量light,用于存储LED灯的状态。
(2)系统初始化
Stm32_Clock_Init(6);
delay_init(72);
LED_Init();
GPIOE->ODR &= ~(0xff << 8);
LED_SEL = 1;
该部分对系统进行初始化,包括设置系统时钟、延时初始化和LED初始化。同时,将GPIOE的ODR寄存器的8~15位清零,即将LED灯全部关闭。LED_SEL的值为1,表示选择两个LED灯进行移位。
③ 灯的移位
light = 0x80;
while( 1 ){
while( 1 )
{
GPIOE->ODR |= (light<<8);
delay_ms( 447 );
light = light>>1;
GPIOE->ODR &= ~(0xff<<8);
GPIOE->ODR |= (light<<8);
delay_ms( 185 );
light = light>>1;
GPIOE->ODR &= ~(0xff<<8);
GPIOE->ODR |= (light<<8);
delay_ms( 142 );
light = light>>1;
GPIOE->ODR &= ~(0xff<<8);
GPIOE->ODR |= (light<<8);
delay_ms( 120 );
light = light>>1;
GPIOE->ODR &= ~(0xff<<8);
GPIOE->ODR |= (light<<8);
delay_ms( 106 );
light = light>>1;
GPIOE->ODR &= ~(0xff<<8);
GPIOE->ODR |= (light<<8);
delay_ms( 94 );
light = light>>1
GPIOE->ODR &= ~(0xff<<8);
GPIOE->ODR |= (light<<8);
delay_ms( 91 );
light = light>>1;
GPIOE->ODR &= ~(0xff<<8);
if( light==0x01 )
{
GPIOE->ODR |= (light<<8);
delay_ms( 90 );
light = light>>1;
GPIOE->ODR &= ~(0xff<<8);
delay_ms( 1 );
light = 0x01;
break;
}
}
while( 1 )
{
GPIOE->ODR |= (light<<8);
delay_ms( 90 );
light = light<<1;
GPIOE->ODR &= ~(0xff<<8);
GPIOE->ODR |= (light<<8);
delay_ms( 91 );
light = light<<1;
GPIOE->ODR &= ~(0xff<<8);
GPIOE->ODR |= (light<<8);
delay_ms( 94 );
light = light<<1;
GPIOE->ODR &= ~(0xff<<8);
GPIOE->ODR |= (light<<8);
delay_ms( 106 );
light = light<<1;
GPIOE->ODR &= ~(0xff<<8);
GPIOE->ODR |= (light<<8);
delay_ms( 120 );
light = light<<1;
GPIOE->ODR &= ~(0xff<<8);
GPIOE->ODR |= (light<<8);
delay_ms( 142 );
light = light<<1;
GPIOE->ODR &= ~(0xff<<8);
GPIOE->ODR |= (light<<8);
delay_ms( 185 );
light = light<<1;
GPIOE->ODR &= ~(0xff<<8);
break;
}
}
该部分是主程序循环,分为两个部分:
第一个while循环:控制LED灯从左往右移位,每次移位一个位置,延时时间逐渐减小。
第二个while循环:控制LED灯从右往左移位,每次移位一个位置,延时时间逐渐增加。
需要注意的是,该代码中的LED控制函数SetLed和LedValue并未给出,需要根据具体的硬件电路进行实现。