1.Delay延时函数
// main //
#include "stm32f10x.h" // Device header
#include "Delay.h" // Device header
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
while(1)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)0);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)1);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);
Delay_ms(500);
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
Delay_ms(500);
GPIO_SetBits(GPIOA, GPIO_Pin_0);
Delay_ms(500);
}
}
首先是最简单的写法,通过GPIO位操作,delay延时函数直接控制led灯闪灭。
第一种是通过bitA0引脚直接控制led。
第二种是用向芯片写入数据控制A0引脚。
第三种是用0/1方便查看。
这样写样子太丑了,所以可以把led模块化。
2.模块化led函数
// main //
#include "stm32f10x.h" // Device header
#include "Delay.h" // Device header
#include "led.h"
int main(void)
{
led_init();
while(1)
{
led_on();
Delay_ms(500);
led_off();
Delay_ms(500);
}
}
// led //
#include "stm32f10x.h" // Device header
void led_init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void led_on(void)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET);
}
void led_off(void)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);
}
用Delay写显然会出现问题,因为delay延时会影响其他函数的正常刷新,导致程序卡死,不能执行其他中断或者函数。
3.中断控制led闪烁
// main //
#include "stm32f10x.h" // Device header
#include "led.h"
#include "time.h"
uint16_t f_500ms1;
uint16_t f_500ms2;
uint16_t cnt_500ms1;
uint16_t cnt_500ms2;
void Timer_Init(void);
void led_mainloop(void);
int main(void)
{
led_init();
Timer_Init();
while(1)
{
led_mainloop();
}
}
void led_mainloop(void)
{
if(f_500ms1 == 0)
led_on();
if(f_500ms2 == 1)
led_off();
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
if(++cnt_500ms1 >= 5)
{
f_500ms1 = 1;cnt_500ms1 = 0;
if(f_500ms1 == 1)
{
f_500ms2 = 1;
if(++cnt_500ms2 >= 2)
{
f_500ms1 = 0;f_500ms2 = 0;cnt_500ms2 = 0;
}
}
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
// timer //
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 1000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE);
}
虽然比第二个麻烦,但是中断是很有必要的。
观察程序发现,用了两个计数器来延时,方法太繁琐了。其实闪烁还有更好的方法,比如说直接把灯500ms取反,这样就能让数据更清晰,不会绕来绕去。
4.中断控制led闪烁取反
// main //
#include "stm32f10x.h" // Device header
#include "led.h"
#include "time.h"
#include "OLED.h"
uint8_t f_1ms;
uint16_t f_500ms;
uint16_t cnt_500ms;
void Timer_Init(void);
void time_mainloop(void);
int main(void)
{
led_init();
Timer_Init();
OLED_Init();
while(1)
{
time_mainloop();
OLED_ShowString(1, 1, "cnt_500ms :");
OLED_ShowNum(1, 14, cnt_500ms, 1);
OLED_ShowString(3, 1, "f_500ms :");
OLED_ShowNum(3, 14, f_500ms, 1);
if(f_500ms==1)
{
led_turn();
f_500ms = 0;
}
}
}
void time_mainloop(void)
{
if(!f_1ms)return;
f_1ms = 0;
if(++cnt_500ms>=5)
{
f_500ms=1;cnt_500ms=0;
}
}
// time //
#include "stm32f10x.h" // Device header
extern uint8_t f_1ms;
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 1000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE);
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
f_1ms = 1;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
// led //
//在原有内容上新增
void led_turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_0) == 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_0);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
}
}
程序肉眼可见的少了,而且逻辑更加清晰。此外还加了OLED屏可以方便观察led值的变化,方便调试程序。
一步一步改进和调试程序的过程还是很有意思的,STM32不同于51单片机,中断得自己设置。
能用和51一样的套路来写,毕竟都是单片机嘛。
STM32更多的是对程序深入的理解和分析,底层程序和逻辑都需要自己来写,自由度也很高。
加!油!