Keil MDK 系列
- Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发
- Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401开发
- Keil MDK STM32系列(三) 基于标准外设库SPL的STM32F407开发
- Keil MDK STM32系列(四) 基于抽象外设库HAL的STM32F401开发
- Keil MDK STM32系列(五) 使用STM32CubeMX创建项目基础结构
- Keil MDK STM32系列(六) 基于抽象外设库HAL的ADC模数转换
- Keil MDK STM32系列(七) 基于抽象外设库HAL的PWM和定时器
配置STM32F401通过PWM和TIM输出音频
机制
- 音频使用一个预生成的的8bit无符号数组, 采样率为8KHz
- 输出包含两部分, 一部分是TIM2产生连续的PWM, PWM分辨率设置为256, 正好对应8bit PCM采样
- 输出的第二部分是TIM3产生的定时中断, 中断的频率正好是8KHz, 每次中断都修改一次PWM的占空比
- 通过调节PWM频率可以调节输出音质, PWM频率越高音质越好(谐振频率越远离音频)
- 通过调节PWM分辨率可以调节音量, PWM分辨率越高, 音量越低
配置STM32CubeMX
选择芯片STM32F401CCU6, 创建新项目
系统时钟
- System Core -> SYS-> Debug: Serial Wire
- System Core -> RCC-> High Speed Clock (HSE): Crystal/Ceramic Resonator 启用外接高速晶振
- Clock Configuration: (配置为最高84MHz)选择外部晶振, 连接HSE和PLLCLK, 在HCLK上输入84回车, 软件会自动调节各节点倍数
PWM(使用TIM2)
- Timers -> TIM2
- Clock Source: Internel Clock, 使用系统的时钟源
- Channel1: PWM Generation CH1
- Counter Settings PWM频率 = 84MHz / (Perscaler + 1) / (Counter Period + 1)
- Perscaler: 0
- Counter Mode: Up
- Counter Period: 255
- Internal Clock Division(CKD): No Division
- auto-reload preload: Enable
- Trigger Output
- Master/Slave Mode (MSM bit): Disable
- Trigger Event Selection: Reset (UG bit from TIMx_EGR)
- PWM Generation Channel 1
- Mode: PWM mode1
- Pulse: 0
- Output compare perload: Enable
- Fast Mode: Disable
- CH Polarity: High
8KHz定时中断(使用TM3)
- Timers -> TIM3
- 勾选 Internal Clock
- Counter Settings
- Prescaler: 0
- Counter Mode: Up
- Counter Period: 10499 # 10500 = 84MHz / 8KHz
- Internal Clock Division (CKD): No division
- auto-reload preload: Disable
- Trigger Output (TRGO) Parameters
- Master/Slave Mode (MSM bit): Disable
- Trigger Envent Selection: Reset
- NVIC Settings
- TIM3 global interrupt: Enable
代码修改
通过STM32CubeMX生成代码后, 需要对main.c添加代码
/* USER CODE BEGIN PV */
uint8_t pwm_buf[] = {125, 125, ..., 126, 125}; // 这里是一个长数组, 可以自己通过工具生成
uint8_t *start = pwm_buf, *end = pwm_buf, *lb = pwm_buf, *rb = (pwm_buf + 27451); // 27451是数组长度
/* USER CODE END PV */
main函数
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
HAL_TIM_Base_Start_IT(&htim3);
/* USER CODE END 2 */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
}
添加定时器中断处理函数
/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, *start++);
if (start == rb) {
start = lb;
}
}
}
/* USER CODE END 4 */
输出效果演示
https://www.bilibili.com/video/BV1pb4y1177L