0x00.章索引
0x01.目录
0x02.背景/声明
得益于STM32系列MCU的外设功能丰富、软件驱动库规范与参考资料多等特性,该系列MCU被广泛运用于各种电子/电气设备中,本文所使用的软件驱动库以官方的HAL(硬件抽象层)库为基础,以结构体指针的方式将各种外设功能进行进一步的封装,让程序具有面对对象编程的部分特性。
本文所使用的驱动库均为自行编写,受限于自身的水平,代码中难免会有不规范的地方,各位多指正~
0x03.开始前…
- STM32Cube IDE
- 正点原子战舰V3板(STM32F103ZET6)
- ST-Link V2
0X04.定时器类和它的成员们
定时器无疑是单片机的所有外设功能中最为重要的几个功能之一,STM32F1的中容量版本单片机具有4个定时器(1个高级定时器+3个通用定时器),定时器的基本原理可以参考STM32之定时器详解,定时器类主要实现了开启/关闭定时器、设置/读取定时器相关寄存器的值与添加PWM或IC通道等功能,详细的结构体定义如下所示:
typedef struct TIMER_COMMON {
TIM_HandleTypeDef tim_handle;
PWM_COMMON pwm_out[4];
IC_CAP_COMMON ic_cap[4];
u32 (*GetCNT)(struct TIMER_COMMON *this);
void (*SetCNT)(struct TIMER_COMMON *this, u32 cnt);
void (*SetPSC)(struct TIMER_COMMON *this, u32 psc);
void (*SetARR)(struct TIMER_COMMON *this, u32 arr);
void (*StartIT)(struct TIMER_COMMON *this);
void (*StopIT)(struct TIMER_COMMON *this);
void (*Start)(struct TIMER_COMMON *this);
void (*Stop)(struct TIMER_COMMON *this);
void (*AddPWMCh)(struct TIMER_COMMON *this, uint32_t Channel,
GPIO_TypeDef *port, uint32_t pin);
void (*AddICAPCh)(struct TIMER_COMMON *this, uint32_t Channel,
GPIO_TypeDef *port, uint32_t pin);
} TIMER_COMMON;
定义了结构体之后,还需要实现该“类”的“构造函数”(也就是对该结构体进行初始化):
TIMER_COMMON* new_TIMER(struct TIMER_COMMON *this, TIM_TypeDef *tim, u8 mode,
u32 psc, u32 arr) {
TIM_ClockConfigTypeDef sClockSourceConfig = { 0 };
TIM_MasterConfigTypeDef sMasterConfig = { 0 };
this = (struct TIMER_COMMON*) calloc(1, sizeof(struct TIMER_COMMON));
this->tim_handle.Instance = tim;
this->tim_handle.Init.Prescaler = psc;
this->tim_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
this->tim_handle.Init.Period = arr;
this->tim_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
this->tim_handle.Init.RepetitionCounter = 0;
this->tim_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&this->tim_handle) != HAL_OK) {
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&this->tim_handle, &sClockSourceConfig)
!= HAL_OK) {
Error_Handler();
}
if (mode == PWM_MODE) {
if (HAL_TIM_PWM_Init(&this->tim_handle) != HAL_OK) {
Error_Handler();
}
} else if (mode == IC_CAPTURE_MODE) {
if (HAL_TIM_IC_Init(&this->tim_handle) != HAL_OK) {
Error_Handler();
}
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&this->tim_handle, &sMasterConfig)
!= HAL_OK) {
Error_Handler();
}
this->StopIT = TIM_StopIT;
this->StartIT = TIM_StartIT;
this->Stop = TIM_Stop;
this->Start = TIM_Start;
this->GetCNT = TIM_Get_CNT;
this->SetCNT = TIM_Set_CNT;
this->SetARR = TIM_Set_ARR;
this->SetPSC = TIM_Set_PSC;
this->AddPWMCh = new_TIM_PWM_Channel;
this->AddICAPCh = new_TIM_IC_CAPTURE_Channel;
return this;
}
本文所描述的例程使用定时器的定时功能,实现频率为2Hz的LED翻转与串口输出,编程的逻辑与使用传统的STD/HAL库类似,首先计算出输出2Hz频率的psc 分频值与arr 自动重装载值的参数,并使用相应的new函数进行初始化:
tim1_up = new_TIMER(tim1_up, TIM1,BASE_MODE, 7199, 4999); //定时器初始化
tim1_up->StartIT(tim1_up);
在配置好定时器后,定时器以2Hz的频率进入定时器溢出回调函数,在定时器中断函数中立起标志位:
void TIM1_UP_IRQHandler(void) {
HAL_TIM_IRQHandler(&tim1_up->tim_handle);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM1) {
time_up = 1;
}
}
在main函数中,实例化LED与UART对象,并根据定时器的标志位进行LED的闪烁与UART串口发送:
int main(void)
{
HAL_Init();
SystemClock_Config();
GPIO_COMMON *led=NULL;
led=new_Gpio(led, GPIOB, GPIO_PIN_5, GPIO_MODE_OUTPUT_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_HIGH);
while (1)
{
led->TogglePin(led);
HAL_Delay(500);
}
}
0x05.小结
TIMER_COMMON是一个实现内容比较多的类,因为定时器本身比较复杂,且后续的PWM输出与INPUT CAPTURE输入捕获都继承于该类,或许有一些定时器的功能是我没有用过的,所以定时器类的接口难免还是会有一些遗漏,可能需要开发者在使用的时候手动修改或添加功能(反正又不是静态库!
附录
Gitee:https://gitee.com/hyjjjjjjjj/STM32_HAL_MODULES
Github:https://github.com/HYJJJJJJJJ/STM32_HAL_MODULES(不怎么更新
1558

被折叠的 条评论
为什么被折叠?



