4、MCAL配置实战:配置一个GPIO引脚、配置一个ADC通道、配置一个PWM输出
好,咱们直接进入正题。前面讲了那么多理论,今天就来点实在的。我带你亲手在EB tresos里把GPIO、ADC、PWM这三个最常用的外设配出来。说实话,这三个配置搞定了,你基本就能应付80%的MCAL开发任务了。
4.1 GPIO引脚配置——从点亮一个LED开始
先讲GPIO。为什么?因为它是基础中的基础。我记得刚入行那会儿,第一个任务就是点灯。你想想看,连灯都点不亮,后面的ADC、PWM根本跑不起来。
4.1.1 新建GPIO配置模块
在EB tresos里,第一步是添加MCAL模块。找到你的工程,右键点击——Add Module——选择Gpt(不对,这里选Gpio)。嗯,别选错了,Gpt是定时器,Gpio才是管脚控制。
添加完成后,你会看到左侧多了一个Gpio的配置树。展开它,里面有GpioPortContainer、GpioChannelContainer这些节点。我个人习惯先把Port配置好,再配Channel。
4.1.2 配置GPIO端口和引脚
双击打开GpioPortContainer,你会看到类似这样的配置项:
GpioPortContainer:
- GpioPort: PortA
GpioPortDirection: GPIO_OUT
GpioPortLevel: GPIO_LOW
GpioPortPull: GPIO_NO_PULL
这里要注意几个关键点:
- GpioPortDirection:输出选GPIO_OUT,输入选GPIO_IN。别搞反了,我见过有人配成输入然后死活点不亮灯。
- GpioPortLevel:初始电平。我建议输出高电平就配HIGH,低电平就配LOW。别配反了,否则上电瞬间可能会闪一下。
- GpioPortPull:上拉/下拉。输入引脚一般配上拉,输出引脚不用配。
4.1.3 配置GPIO通道
配完Port,接下来配Channel。Channel其实就是把Port和具体的引脚号绑定起来。比如你想用PA0这个引脚:
GpioChannelContainer:
- GpioChannel: LedChannel
GpioChannelId: 0 // 对应PA0
GpioChannelPort: PortA
GpioChannelDirection: GPIO_OUT
这里有个小技巧:GpioChannelId的值要和芯片的引脚号对应。比如PA0就是0,PA1就是1,以此类推。别问我为什么有人会填成10,反正我见过。
4.1.4 生成代码并验证
配置完成后,按Ctrl+B生成代码。你会看到生成了Gpio.c和Gpio.h。在代码里调用:
// 点亮LED
Gpio_WriteChannel(LedChannel, GPIO_HIGH);
// 读取按键
Gpio_LevelType level = Gpio_ReadChannel(KeyChannel);
嗯,就这么简单。但你要记住,调用前一定要先初始化:
Gpio_Init(&Gpio_Config);
不初始化就调用?我保证你的程序会跑飞。
4.2 ADC通道配置——让芯片学会“感觉”
GPIO只能读高低电平,但如果你想读一个模拟电压值,比如温度传感器的输出,那就得上ADC了。
4.2.1 添加ADC模块
同样,在EB tresos里添加Adc模块。展开后你会看到AdcConfigSet、AdcHwUnit、AdcChannel这些节点。
我个人习惯先配AdcHwUnit,也就是ADC硬件单元。比如你的芯片有ADC0和ADC1,你要选哪个?
AdcHwUnit:
- AdcHwUnitId: 0
AdcResolution: ADC_12_BIT
AdcPrescale: 4
AdcClockSource: ADC_CLOCK_PCLK
这里AdcResolution选12位还是10位?我建议选12位,精度更高。但要注意,转换时间会变长。如果你需要高速采样,那就选10位。
4.2.2 配置ADC通道
接下来配具体的通道。比如你想用ADC0的通道3来采集电压:
AdcChannel:
- AdcChannelId: 3
AdcChannelHwUnit: 0
AdcChannelSamplingTime: 10
AdcChannelResultAlignment: ADC_ALIGN_RIGHT
这里有个关键参数——AdcChannelSamplingTime。这个值决定了采样电容的充电时间。如果配得太小,采样值会偏小;配得太大,转换速度又慢。我一般取10~20个时钟周期,具体看芯片手册。
4.2.3 配置ADC转换组
ADC通道配好了,但怎么触发转换呢?这就需要配AdcGroup了。一个Group可以包含多个通道,一次性转换完。
AdcGroup:
- AdcGroupId: 0
AdcGroupConversionMode: ADC_CONV_MODE_ONESHOT
AdcGroupTriggerSrc: ADC_TRIGG_SRC_SW
AdcGroupChannelList: [3, 4, 5] // 同时转换通道3、4、5
这里AdcGroupTriggerSrc我选了软件触发,方便调试。实际项目中可能会用硬件定时器触发,比如每1ms转换一次。
4.2.4 读取ADC值
生成代码后,读取ADC值也很简单:
// 启动转换
Adc_StartGroupConversion(0);
// 等待转换完成
while(Adc_GetGroupStatus(0) != ADC_COMPLETED);
// 读取结果
uint16_t adcValue;
Adc_ReadGroup(0, &adcValue);
注意,Adc_ReadGroup返回的是原始值,不是电压值。要转换成电压,需要自己算:
float voltage = (float)adcValue * 3.3f / 4095.0f; // 12位ADC,参考电压3.3V
4.3 PWM输出配置——让芯片“动”起来
最后讲PWM。PWM说白了就是让引脚输出一个频率固定、占空比可调的方波。可以用来控制电机速度、LED亮度、蜂鸣器音量等等。
4.3.1 添加PWM模块
在EB tresos里添加Pwm模块。注意,PWM通常依赖定时器,所以你可能还需要配一个Gpt模块。不过有些芯片的PWM是独立的,比如Infineon的GTM模块。
展开Pwm配置树,你会看到PwmChannel节点。双击打开:
PwmChannel:
- PwmChannelId: 0
PwmChannelPeriod: 1000
PwmChannelDutyCycle: 500
PwmChannelPolarity: PWM_HIGH
PwmChannelIdleState: PWM_LOW
这里解释几个参数:
- PwmChannelPeriod:周期,单位是定时器时钟周期数。比如定时器时钟是1MHz,周期配1000,那PWM频率就是1kHz。
- PwmChannelDutyCycle:占空比,单位也是时钟周期数。配500就是50%占空比。
- PwmChannelPolarity:极性。选HIGH表示高电平有效,选LOW表示低电平有效。
- PwmChannelIdleState:空闲状态。PWM停止输出时,引脚保持什么电平。
4.3.2 配置PWM波形输出
有时候你需要输出特定波形,比如中心对齐的PWM或者边沿对齐的PWM。这需要在PwmChannelClass里设置:
PwmChannelClass: PWM_VARIABLE_PERIOD
PwmChannelOutputType: PWM_EDGE_ALIGNED
我个人习惯用边沿对齐,因为简单。中心对齐虽然谐波更少,但配置起来麻烦一些。
4.3.3 动态调整占空比
生成代码后,你可以随时调整占空比:
// 设置占空比为75%
Pwm_SetDutyCycle(0, 750);
// 设置周期为2000(频率变为500Hz)
Pwm_SetPeriodAndDuty(0, 2000, 1000);
注意,Pwm_SetDutyCycle只改占空比,不改周期。Pwm_SetPeriodAndDuty可以同时改周期和占空比。但改周期时,要确保占空比不超过新周期,否则PWM会输出恒高或恒低。
4.4 三个模块的协同工作
讲到这里,你可能觉得三个模块是独立的。其实在实际项目中,它们经常协同工作。比如:
- GPIO读取按键状态
- ADC采集电位器电压
- PWM根据ADC值调整LED亮度
代码大概长这样:
void main(void) {
// 初始化所有模块
Gpio_Init(&Gpio_Config);
Adc_Init(&Adc_Config);
Pwm_Init(&Pwm_Config);
while(1) {
// 读取按键
if(Gpio_ReadChannel(KeyChannel) == GPIO_LOW) {
// 启动ADC转换
Adc_StartGroupConversion(0);
while(Adc_GetGroupStatus(0) != ADC_COMPLETED);
uint16_t adcValue;
Adc_ReadGroup(0, &adcValue);
// 将ADC值映射到PWM占空比
uint16_t duty = (uint16_t)((uint32_t)adcValue * 1000 / 4095);
Pwm_SetDutyCycle(0, duty);
}
}
}
你看,三个模块配合起来,就能实现一个简单的调光器。这就是MCAL配置的魅力——把硬件抽象成API,上层应用只管调用就行。
4.5 本章小结
今天咱们把GPIO、ADC、PWM三个模块的配置都过了一遍。说实话,这些配置在EB tresos里操作起来并不复杂,关键是要理解每个参数的含义。我建议你打开EB tresos,照着上面的步骤亲手配一遍。光看不练,永远学不会。
下一章我会讲更复杂的模块——SPI和I2C的配置。这两个模块涉及到通信协议,配置起来会稍微复杂一些。但别担心,我会把每个步骤拆开来讲。
嗯,今天就到这里。有问题随时找我。