文章目录
实验目的
通过定时器1实现间隔100ms的双向流水灯
定时器相关知识
硬件设计
原理图
STM32CubeMX配置
1、使用PC8~PC15控制LED灯亮灭,LD1 - LD8为引脚的别名
2、使用PD2控制锁存器,LEDLOCK为PD2的别名
3、HSE选用石英/陶瓷谐振器
4、GPIO均设置为输出模式,使用默认的GPIO配置
5、时钟配置为80MHz
6、配置定时器TIM1,时钟源选择内部时钟
7、配置定时时间
Prescaler=1000-1
Counter Period=80-1
定时器时钟频率为80MHZ,即80 000 000HZ
定时时间 = (Prescaler+1 ) X (Counter Period+1) X 1/ 定时器时钟频率(信号的周期)
定时时间 =1000 X 80 / 80000000=0.001s=1ms
知识补充:
a. APB2负责AD,I/O,高级TIM,串口1。
APB1负责DA,USB,SPI,I2C,CAN,串口2345,普通TIM
TIM1是高级定时器,包括了基本定时器和通用定时器的所有功能。
b. HZ是赫兹,频率单位,1MHZ(兆赫)=1000KHZ(千赫)=1000000HZ
c. 定时器发生中断时间的计算方法:
定时时间 = (Prescaler+1 ) X (Counter Period+1) X 1/ 定时器时钟频率(信号的周期)
时钟信号1KHz,Prescaler为9,Counter Period为999,定时时间?
定时时间=10x1000x1/1000=10s=10000ms=10s
Keil配置
Keil配置参考ADC篇,不赘述了
Keil配置
软件设计
1、引用头文件
main.c中添加头文件。(注:所有代码放到BEGIN和END之间,防止重新生成代码时将原来的代码覆盖掉)
#include "lcd.h"
2、编写定时器回调函数
//实现间隔为100ms的流水灯
///
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //周期运行回调,配置定时进入中断
{
static uint16_t led = 0x0001, i = 0, flag = 0 ,counter = 0;
if(++counter == 100){ //100*1ms=100ms
counter = 0;
GPIOC->ODR = ~(led << (8 + i)); //通过位运算设置输出端口寄存器ODR的值,从而控制LED的亮灭
HAL_GPIO_WritePin(LEDLOCK_GPIO_Port, LEDLOCK_Pin, GPIO_PIN_SET); //打开锁存器 GPIOD->BSRR |= GPIO_PIN_2;
__nop();__nop();__nop(); //一个__nop()函数延时一个机器周期的时间
HAL_GPIO_WritePin(LEDLOCK_GPIO_Port, LEDLOCK_Pin, GPIO_PIN_RESET); //关闭锁存器 GPIOD->BRR |= GPIO_PIN_2;
if(flag == 0) //flag作为流水灯方向的标志位 当flag = 0时,LD1流向LD8 ; 当flag = 1时,LD8流向LD1
{
++i;
if(i == 7){ flag = 1; }
}
else
{
--i;
if(i == 0){ flag = 0; }
}
}
HAL_TIM_Base_Start_IT(&htim1); //使能定时器1中断
}
3、全灭LED灯并锁存,开启定时器1中断
HAL_GPIO_WritePin(GPIOC,LD1_Pin|LD2_Pin | LD3_Pin|LD4_Pin | LD5_Pin|LD6_Pin |LD7_Pin|LD8_Pin, GPIO_PIN_SET); //LED全灭 等价于GPIOC->ODR = 0xFFFF;
HAL_GPIO_WritePin(LEDLOCK_GPIO_Port, LEDLOCK_Pin, GPIO_PIN_SET); //打开锁存器 等价于GPIOD->BSRR |= GPIO_PIN_2;
__nop();__nop();__nop(); //一个__nop()函数延时一个机器周期的时间 表示空循环一个机器指令的时间,STM32中1/80M=0.0125ns ; C51中12M中表示1us;6M中表示2us;24M中表示0.5us STM32中1/80M=0.0125ns
HAL_GPIO_WritePin(LEDLOCK_GPIO_Port, LEDLOCK_Pin, GPIO_PIN_RESET); //关闭锁存器 等价于GPIOD->BRR |= GPIO_PIN_2;
HAL_TIM_Base_Start_IT(&htim1); //使能定时器1中断
4、LCD初始化及LCD相关配置、显示字符串
LCD_Init();
LCD_Clear(Blue);
LCD_SetBackColor(Blue);
LCD_SetTextColor(White);
LCD_DisplayStringLine(Line0, (uint8_t *)" ");
LCD_DisplayStringLine(Line1, (uint8_t *)" ");
LCD_DisplayStringLine(Line2, (uint8_t *)" TIM Test ");
LCD_DisplayStringLine(Line3, (uint8_t *)" ");
LCD_DisplayStringLine(Line4, (uint8_t *)" ");
LCD_SetBackColor(White);
LCD_SetTextColor(Blue);
LCD_DisplayStringLine(Line5, (uint8_t *)" ");
LCD_DisplayStringLine(Line6, (uint8_t *)" TIM1:100ms ");
LCD_DisplayStringLine(Line7, (uint8_t *)" ");
LCD_DisplayStringLine(Line8, (uint8_t *)" ");
LCD_DisplayStringLine(Line9, (uint8_t *)" ");
5、tim.c文件中
可以看到配置效果
/* TIM1 init function */
void MX_TIM1_Init(void) //(999+1)*(79+1)/80 000 000=1000*80/80 000 000 =0.001s=1ms
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
htim1.Instance = TIM1;
htim1.Init.Prescaler = 1000-1; //设置为1000-1
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 79; //设置为80-1
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sBreakDeadTimeConfig.BreakAFMode = TIM_BREAK_AFMODE_INPUT;
sBreakDeadTimeConfig.Break2AFMode = TIM_BREAK_AFMODE_INPUT;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
}
函数位置
TIM
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);
GPIO
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
LCD
void LCD_Init(void);
void LCD_SetTextColor(vu16 Color);
void LCD_SetBackColor(vu16 Color);
void LCD_Clear(u16 Color);
void LCD_DisplayStringLine(u8 Line, u8 *ptr);
实验现象
可以看到双向流水灯现象,即LD1流向LD8,LD8流向LD1,如此循环下去