-
背景
-
外设的驱动逻辑
外设包括:定时器、I2C、SPI、UART、RTC、ADC、DAC等等。由于每块芯片的外设都有所不同(地址、功能等),所以驱动代码天然需要进行功能分层。一方面保证驱动的逻辑性,一方面可以将代码功能进行解耦(如下表目录所示,新增芯片时只需新增LowLevel层的文件)。
分层如下:- Low Level (LL) Layer
- Hardware Abstraction Layer (HAL)
- Driver Layers
分层 功能 目录 Low Level 与硬件打交道的最底层,向HAL层提供服务 component\hal\esp32\include\hal HAL 基于ll层,将ll层进行逻辑封装,向Driver层提供服务 component\hal Driver 向上层应用提供服务 componnet\driver 举例:
driver层timer.c 提供定时器的操作接口:timer_init
timer_init会调用hal层的定时器接口:timer_hal_reset_periph
timer_hal_reset_periph会调用ll层的接口:timer_ll_intr_disable -
PCNT-- pulse counter 统计上升和下降沿的个数
esp32包含多个 pcnt unit,每个pcnt unit包含一个16bit计数器 和 两个channel,每个channel接入两个信号:ctrl、sig
学习中对于ctrl信号的模式(keep、inverse、hold)比较困惑,下边结合官方示例说明:
-
官方示例1
examples\peripherals\pcnt\pulse_count_event\main\pcnt_event_example_main.c
该例子通过IO18产生PWM波形(1HZ),IO4作为PCNT的sig输入与IO18短接,IO5作为PCNT的ctrl输入接地// sig信号的计数模式 .pos_mode = PCNT_COUNT_INC, // 上升沿增加计数器 .neg_mode = PCNT_COUNT_DIS, // 下降沿不操作计数器 // ctrl信号的控制模式 .lctrl_mode = PCNT_MODE_REVERSE, // 反转模式:当ctrl为低电平,pos_mode配置为增加计数时,那么上升沿到来时减少计数器 // 当ctrl为低电平,pos_mode配置为减少计数时,那么上升沿到来时增加计数器 // 当ctrl为低电平,neg_mode配置为增加计数时,那么上升沿到来时减少计数器 // 当ctrl为低电平,neg_mode配置为减少计数时,那么上升沿到来时增加计数器 .hctrl_mode = PCNT_MODE_KEEP, // 保持模式:当ctrl为高电平时,保持原有的上升下降计数模式
综上,由于IO5接地,所以ctrl的信号控制模式固定为翻转模式。所以每隔1s读到计数器值在递减,达到边界值后,计数器恢复0后继续减1递减
// 样例输出 Current counter value :-1 Current counter value :-2 Current counter value :-3 Current counter value :-4 Event PCNT unit[0]; cnt: -5 THRES0 EVT Current counter value :-5 Current counter value :-6 Current counter value :-7 Current counter value :-8 Current counter value :-9 Event PCNT unit[0]; cnt: 0 L_LIM EVT ZERO EVT Current counter value :0 Current counter value :-1
-
官方示例2
examples\peripherals\pcnt\rotary_encoder\main\rotary_encoder_example_main.c
该用例使用EC11(旋转编码器)作为PCNT的输入 (旋转编码器原理),将旋钮的物理选转转化为正负数。PCNT两个通道的关键配置罗列如下// Configure channel 0 pcnt_config_t dev_config1 = { .pulse_gpio_num = GPIO14, // channel 0/1 输入颠倒 .ctrl_gpio_num = GPIO15, .channel = PCNT_CHANNEL_0, // channel 0/1 .pos_mode = PCNT_COUNT_DEC, // channel 0/1 配置相反 .neg_mode = PCNT_COUNT_INC, // channel 0/1 配置相反 .lctrl_mode = PCNT_MODE_REVERSE, .hctrl_mode = PCNT_MODE_KEEP, }; pcnt_config_t dev_config2 = { .pulse_gpio_num = GPIO14, .ctrl_gpio_num = GPIO15, .channel = PCNT_CHANNEL_1, .pos_mode = PCNT_COUNT_INC, .neg_mode = PCNT_COUNT_DEC, .lctrl_mode = PCNT_MODE_REVERSE, .hctrl_mode = PCNT_MODE_KEEP, };
当顺时针旋转EC11时,IO14领先IO15相位90度(参见后边的棘轮定位基准点图,实际应该小于90°),波形如下
t0时刻:channel0配置生效,IO14作为sig信号是上升沿(PCNT_COUNT_DEC),此时IO15作为ctrl信号为低电平(PCNT_MODE_REVERSE),因此此时增加计数器t1时刻:channel1配置生效,IO15作为sig信号是上升沿(PCNT_COUNT_INC),测试IO14作为Ctrl信号为高电平(PCNT_MODE_KEEP),因此此时增加计数器
t2时刻:channel0配置生效,IO14作为sig信号是下降沿(PCNT_COUNT_INC),此时IO15作为ctrl信号为高电平(PCNT_MODE_KEEP),因此此时增加计数器
t3时刻:channel1配置生效,IO15作为sig信号为下降沿(PCNT_COUNT_DEC),此时IO15作为ctrl信号为低电平(PCNT_MODE_REVERSE),因此此时增加计数器
逆时针旋转EC11时,每次上升或下降沿都是递减计数器
疑问来了 为何是以4为单位进行计数。示例打印如下
I (181323) example: Encoder value: 0
I (182323) example: Encoder value: 0
I (183323) example: Encoder value: -12
I (184323) example: Encoder value: -18
I (185323) example: Encoder value: -24
I (188323) example: Encoder value: 4
I (189323) example: Encoder value: 8查看ec11某款产品参数:定位数(旋钮棘轮个数)为脉冲数(脉冲个数/360°)的两倍,因此计数器的最小增长单位应该是2才对,这点理解不了。(可能样例使用的是定位数=脉冲数的选转编码器)
此外说明书中也没有明确指出定位基准点(棘轮卡位处)与B signal上升下降沿完全对齐。
-
-
MCPWM - motor control pulse width module 电机脉宽调制
该外设学习使用的示例1为有刷电机控制: examples\peripherals\mcpwm\mcpwm_brushed_dc_control
1)需要对PID(proportional integral differential 百分比 积分 微分)控制模式有基本的了解 此外两种数字处理方式(增量、位置)的推导也很简单
该示例通过将马达与旋转编码器(EC11)相连,旋转编码器与PCNT相连,来感知马达的转速;通过PID的控制模式,利用MCPWM将马达调整至期望转速。
该外设学习使用的示例2为无刷电机控制: examples\peripherals\mcpwm\mcpwm_bldc_hall_control
需掌握:
1 无刷电机(brushless direct current motor)的基本原理
2 无刷电机与霍尔传感器的配合
3 死区的概念 避免桥驱动的过程中出现上桥和下桥同时打开的情况
PS:航模中使用的无刷电机有3根接线,并无3根霍尔传感器的输出线是为什么?
利用反电动势原理(楞次定律)无刷电机的转子转动时,会在定子的线圈中产生反电动势,电调通过检测反电动势来识别定子的位置,从而控制定子线圈的通电顺序。
Q&A:
-
全局变量 TIMERG0 的定义未找到
\esp-idf-v4.4.1\components\soc\esp32\include\soc\timer_group_struct.htimer_group_struct.h
extern timg_dev_t TIMERG0; extern timg_dev_t TIMERG1;
根据该变量的使用,可以通过它控制定时器。
其实该变量的定义在ld文件 esp32.peripherals.ld 中:
属于外设地址范围