SOP8封装的MM32G0001A6T提供的管脚虽然只有8个,但依然可以使用除了电源、下载烧录用之外的GPIO向外输出PWM信号(当然要和复用功能匹配才行)。
实现PWM,当然可以使用软件通过模拟当时实现,但使用定时器还是最便捷的方式。所以在测试输出PWN之前,先理解MM32G0001A6T的定时器外设。
一、MM32G0001A6T的定时器
MM32G0001A6T内部包含了1个高级定时器、1个通用定时器、1个基本定时器、1个看门狗定时器和1个系统嘀嗒定时器。
1、高级定时器TIM1
高级控制定时器是由 16 位计数器、4 个比较通道以及三相互补 PWM 发生器组成,它具有带死区插入的互补 PWM 输出,还可以被当成完整的通用定时器。四个独立的通道可以用于:
• 输出比较
• 产生 PWM(边缘或中心对齐模式)
• 单脉冲输出
配置为 16 位通用定时器时,它与 TIM3 定时器具有相同的功能。配置为 16 位 PWM 发生器时,它具有全调制能力(0 ∼ 100%)。
很多功能都与通用的 TIM 定时器相同,内部结构也相同,因此高级控制定时器可以通过定时器链接功能与 TIM 定时器协同操作,提供同步或事件链接功能。
2、通用定时器(TIM3)
产品中内置了 1 个 16 位通用定时器(TIM3)。定时器有一个 16 位的自动加载递加/递减计数器、一个 16 位的预分频器和 4 个独立的通道,每个通道都可用于输入捕获、输出比较、PWM 和单脉冲模式输出。通用定时器还能通过定时器链接功能与高级控制定时器共同工作,提供同步或事件链接功能。
这些定时器还能够处理增量编码器的信号,也能处理 1 ~ 4 个霍尔传感器的数字输出。每个定时器都 PWM 输出或作为简单时间基准。
3、基本定时器(TIM14)
产品中内置 1 个基本定时器(TIM14),每个定时器有一个 16 位计数器,支持自动重载,仅支持递增计数。定时器有一个 16 位预分频器和 1 个独立通道,每个通道可用于输入捕捉、输出比较、PWM 输出或单脉冲输出。
4、独立看门狗(IWDG)
独立的看门狗是基于一个 12 位的递减计数器和一个 8 位的预分频器,它由一个内部独立的 40KHz 的振荡器提供时钟。因为这个振荡器独立于主时钟,所以它可运行于停机和待机模式。它可以用在系统发生问题时复位整个系统或作为一个自由定时器为应用程序提供超时管理。通过选项字节可以配置成是软件或硬件启动看门狗。在调试模式下,计数器可以被冻结。
5、系统时基定时器(Systick)
• 24 位的递减计数器
• 自动重加载功能
• 当计数器为 0 时能产生一个可屏蔽系统中断
• 可编程时钟源
二、总结
根据手册提供的资料,在MM32G0001A6T提供的定时器中,定时器1、定时器3、定时器14都有能力提供PWM输出。
那么我就先尝试用定时器3产生PWM信号,找个GPIO口输出。使用定时器3的原因是:芯片有PA1,PA2,PA3,PA15可以使用,PA13和PA14是下载用接口,不敢动。而PA1,PA2,PA3,PA15中,根据端口复用,PA2在AF4复用功能下,可以作为定时器3的通道2使用,PA15在AF4复用功能下,可以作为定时器3的通道3使用。
程序如下:
void TIM3_Configure(void) { GPIO_InitTypeDef GPIO_InitStruct; TIM_OCInitTypeDef TIM_OCInitStruct; TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct; /* Compute the value to be set in ARR regiter to generate signal frequency at 100 Khz */ // 设置定时周期 uint32_t TimerPeriod = (RCC_GetPCLK1Freq() / 100000 ) - 1; /* Compute CCR2 value to generate a duty cycle at 40% for channel 2 */ // 配置通道2 uint32_t Channel2Pulse = ((uint32_t)400 * (TimerPeriod - 1)) / 1000; // 配置通道3 /* Compute CCR3 value to generate a duty cycle at 25% for channel 3 */ uint32_t Channel3Pulse = ((uint32_t)250 * (TimerPeriod - 1)) / 1000; // 允许总线 RCC_APB1PeriphClockCmd(RCC_APB1ENR_TIM3, ENABLE); // 配置定时器1 TIM_TimeBaseStructInit(&TIM_TimeBaseStruct); TIM_TimeBaseStruct.TIM_Prescaler = 0; TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数 TIM_TimeBaseStruct.TIM_Period = TimerPeriod; // 设定周期 TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStruct.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStruct); TIM_OCStructInit(&TIM_OCInitStruct); TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStruct.TIM_Pulse = 0; TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Set; TIM_OCInitStruct.TIM_Pulse = Channel2Pulse; TIM_OC2Init(TIM3, &TIM_OCInitStruct); TIM_OCInitStruct.TIM_Pulse = Channel3Pulse; TIM_OC3Init(TIM3, &TIM_OCInitStruct); RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE); // 使用通道2,复用功能AF4,对PA2 GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_4); /* TIM3_CH2 */ // 使用通道2,复用功能AF4,对PA2 GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_4); /* TIM3_CH3 */ GPIO_StructInit(&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_15; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStruct); TIM_Cmd(TIM3, ENABLE); TIM_CtrlPWMOutputs(TIM3, ENABLE); } void TIM3_PWM_Output_Sample(void) { // 配置Timer1 TIM3_Configure(); while (1) { // 翻转LED GPIO_WriteBit(GPIOA, GPIO_Pin_3, GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_3) ? Bit_RESET : Bit_SET); // 延时100mS PLATFORM_DelayMS(100); } } int main(void) { // 初始化设备 PLATFORM_Init(); // 输出PWM TIM3_PWM_Output_Sample(); while (1) { } }
自己搭建的测试板:
测得的波形如下: