在mm32f3270上为micropython创建PWM模块(1)
PS: 对于以英文作为主要编码工具的软件工程师, 即使在中文编辑环境写文档, 使用英文标点符号还是比较方便的.
Requirements
micropython的开发手册中提供了关于PWM模块的API参考设计规范, 见:
https://docs.micropython.org/en/latest/library/machine.PWM.html
我又在micropython的代码仓库里看了一下, 只有rp2/esp32/esp8266三个平台上有machine_pwm.c的实现, 官方开发手册中的实现同rp2保持一致, esp系列另一套相近的实现.
在设计PWM模块的时候, 要考虑多个PWM公用同一个定时器的问题, 并且定时器的引脚有时不会很多, 不能支持很多PWM, 那么如果要支持更多的PWM输出通道, 还有可能考虑到多个定时器分组的问题.
rp2的实现有点炫富的味道, 从代码上看, 可能rp2的1定时器有8个通道(强大的定时器IP), 也可能是有8个定时器(强大的SOC), 每个定时器仅引出一路通道(A和B算一个).
STATIC machine_pwm_obj_t machine_pwm_obj[] = {
{{&machine_pwm_type}, 0, PWM_CHAN_A},
{{&machine_pwm_type}, 0, PWM_CHAN_B},
{{&machine_pwm_type}, 1, PWM_CHAN_A},
{{&machine_pwm_type}, 1, PWM_CHAN_B},
{{&machine_pwm_type}, 2, PWM_CHAN_A},
{{&machine_pwm_type}, 2, PWM_CHAN_B},
{{&machine_pwm_type}, 3, PWM_CHAN_A},
{{&machine_pwm_type}, 3, PWM_CHAN_B},
{{&machine_pwm_type}, 4, PWM_CHAN_A},
{{&machine_pwm_type}, 4, PWM_CHAN_B},
{{&machine_pwm_type}, 5, PWM_CHAN_A},
{{&machine_pwm_type}, 5, PWM_CHAN_B},
{{&machine_pwm_type}, 6, PWM_CHAN_A},
{{&machine_pwm_type}, 6, PWM_CHAN_B},
{{&machine_pwm_type}, 7, PWM_CHAN_A},
{{&machine_pwm_type}, 7, PWM_CHAN_B},
};
如此, 它当然很好解决PWM多路复用的问题, 要么全复用, 要么全独立.
顺便翻了一下raspberry pico的介绍, 好家伙, 这个芯片几乎就是为这块专属的板子定义的, 而这个板子的定义就是为了服务micropython.
rp2的machine_pwm实现专门把duty的可调范围扩展到65536. 甚至不惜耗费算力在CM0+的内核上做乘除法进行换算ns等.
// Set the frequency, making "top" as large as possible for maximum resolution.
// Maximum "top" is set at 65534 to be able to achieve 100% duty with 65535.
#define TOP_MAX 65534
mp_int_t freq = mp_obj_get_int(args[1]);
uint32_t div16_top = 16 * source_hz / freq;
整体代码实现得也不是很好看. 但无所谓, 只要板子最终的功能出来就行, 软件可能也不是为了给用户做二次开发的. 大气! 但做通用MCU不能这么搞. 所以最终我还是考虑参考esp系列的实现模型.
esp32和esp8266使用的是同一套接口:
STATIC const mp_rom_map_elem_t pyb_pwm_locals_dict_table[] =
{
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pyb_pwm_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&pyb_pwm_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&pyb_pwm_freq_obj) },
{ MP_ROM_QSTR(MP_QSTR_duty), MP_ROM_PTR(&pyb_pwm_duty_obj) },
};
从命名风格上看, PWM模块还归属在pyb模块下, 而不是后来明文约定的machine, 说明这个还是早期的实现. 问题不大, 我用它的实现模型, 再挂在machine模块下.
在实现基本API的同时, 我会提前约定:
- duty的有效输入范围为0-1000, 表示千分比. 这样可以固定定时器的计数周期就是1000, 但是频率可调
- 通过TIM3和TIM3和TIM4, 总共引出6个通道, 尝试设计一种多定时器组合的架构, 同时要考虑好共用一个定时器的多个PWM通道之间的耦合问题.
// start the PWM subsystem if it's not already running
if (!pwm_inited) {
pwm_init();
pwm_inited = true;
}
呵呵, 我也是这么想的,避免重复初始化共享时基.
- 关于实例化传参, 还是遵循micropython中流行的做法, 可以接受: 引脚名/全局通道号/同类对象
未完待续 …