
昨天完成了LCD屏幕驱动实验当时LCD背光直接用GPIO引脚高低电平控制只有亮/灭两档无法调节亮度。今天继续基于IM6ULL开发板学习两大常用外设PWM脉冲宽度调制、ADC模数转换器。本次实验目标ADC采集模拟电压在LCD屏幕实时打印ADC原始(Voltage \frac{采样值}{4096} \times 3.3V)与电压同时使用PWM输出信号可以用来控制LED/LCD背光亮度实现平滑调光。一、基础概念理解1. PWM 脉冲宽度调制PWMPulse Width Modulation脉冲宽度调制。频率单位HZ周期公式周期 1 / 频率例1KHz 1000Hz对应周期就是1ms。占空比在一个周期内高电平持续时间占总周期时间的比例。原理利用人眼视觉暂留效应快速不断切换高低电平。占空比越大等效平均电压越高LED越亮占空比越小亮度越低。输出极性波形极性会影响高低电平对应的占空比。30%占空比高电平持续300单位时间低电平70070%占空比高电平持续700单位时间低电平300。对比昨天LCD背光GPIO控制GPIO只有0和1两种状态只能开关PWM可以实现连续多级亮度调节。2. ADC 模数转换器ADCAnalog-to-Digital Converter模拟信号转数字信号。IM6ULL片上ADC为12位ADC采样电压范围0~3.3V采样数字值范围0 ~ 4095。电压计算公式Voltage \frac{(Voltage \frac{采样值}{4096} \times 3.3V)}{4096} \times 3.3V可以用来采集电位器、光敏电阻这类模拟传感器把连续变化的模拟电压转换成CPU能够识别的数字量。二、完整代码解析本次代码分为pwm.c、adc.c、main.c三部分全部基于IM6ULL寄存器直接操作裸机开发不依赖高层SDK封装。1. pwm.c 完整代码#includepwm.h#includeMCIMX6Y2.h#includefsl_iomuxc.h#includeinterrupt.hstaticfloatg_dc0.5;voidset_dc(floatdc){g_dcdc;}floatget_dc(void){returng_dc;}voidset_rotia(floatdc){unsignedinttempPWM1-PWMPR;inti0;for(i0;i4;i){PWM1-PWMSARtemp*(1-dc);}}voidpwm1_irq_handler(void){if((PWM1-PWMSR(13))!0){set_rotia(g_dc);//清除中断标志IMX6ULL PWM为写1清零PWM1-PWMSR|(13);}}voidpwm1_init(void){//复用功能配置GPIO1_IO08复用为PWM1_OUTIOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_PWM1_OUT,0);//电气特性IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_PWM1_OUT,0xB9);//PWM模块复位PWM1-PWMCR|(13);while((PWM1-PWMCR(13))!0);//PWM1寄存器配置PWM1-PWMCR0;PWM1-PWMCR|(126)|(116)|(654)|(31);PWM1-PWMIR|(10);PWM1-PWMPR998;//3.设置中断优先级GIC_SetPriority(PWM1_IRQn,0);//4.注册中断处理函数system_interrupt_register(PWM1_IRQn,pwm1_irq_handler);//5.设置中断使能GIC_EnableIRQ(PWM1_IRQn);set_rotia(g_dc);//开启PWM模块PWM1-PWMCR|(10);}PWM代码说明引脚复用GPIO1_IO08设置为PWM1输出功能配置PAD电气属性。外设复位PWM上电需要软件复位循环等待复位完成。时钟与周期配置PWMPR寄存器配置PWM周期这里赋值998。中断配置GIC配置PWM中断优先级、注册中断服务函数开启IRQ。中断服务函数pwm1_irq_handlerPWM周期溢出触发中断在中断内部更新PWMSAR比较寄存器动态修改PWM占空比。接口函数set_dc()全局变量设置目标占空比get_dc()读取当前占空比set_rotia()根据占空比计算PWMSAR的值1-dc和PWM输出极性匹配2. adc.c 完整代码#includeadc.h#includeMCIMX6Y2.h#includefsl_iomuxc.h#includestdio.hintCalibration_self(void){ADC1-GS|(11);ADC1-GC|(17);while((ADC1-GC(17))!0);return((ADC1-GS(11))0);}voidadc1_init(void){//复用功能配置GPIO1_IO01作为ADC模拟输入IOMUXC_SetPinMux(IOMUXC_GPIO1_IO01_GPIO1_IO01,0);//电气特性IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO01_GPIO1_IO01,IOMUXC_SW_PAD_CTL_PAD_PKE(1));IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO01_GPIO1_IO01,IOMUXC_SW_PAD_CTL_PAD_PUE(0));//ADC寄存器配置ADC1-HC[0]~(17);ADC1-CFG0;ADC1-CFG|(22)|(30);ADC1-GC0;ADC1-GC|(10);//ADC自校准必须执行消除硬件偏移误差printf(Calibration_self()0? Calibration completed normally.\n:Calibration failed.\n);}unsignedshortget_adc_value(void){ADC1-HC[0]0x1F;ADC1-HC[0]1;while((ADC1-HS(10))0);return(unsignedshort)(ADC1-R[0]0xFFF);}floatget_voltage(void){unsignedshorttempget_adc_value();return(float)temp/4096.0f*3.3f;}ADC代码说明引脚配置GPIO1_IO01配置为模拟输入引脚配置PAD上下拉、使能。ADC初始化配置ADC时钟分频、开启ADC电源。自校准 Calibration_selfADC上电必须执行自校准消除硬件带来的采样误差校准失败采样结果会漂移不准。get_adc_value()启动一次ADC转换等待转换完成读取12bit原始采样数据。get_voltage()将12位数字量换算成实际电压电压 adc原始值 /4096.0 *3.3。3. main.c 完整代码#includeMCIMX6Y2.h#includefsl_iomuxc.h#includeled.h#includebeep.h#includekey.h#includeinterrupt.h#includeclock.h#includeepit.h#includegpt.h#includeuart.h#includestdio.h#includestring.h#includei2c.h#includeat24c02.h#includelm75.h#includelcd.h#includeframebuffer.h#includepwm.h#includeadc.hintmain(void){system_interrupt_init();clock_init();led_init();beep_init();key_init();//epit1_init();gpt1_init();uart1_init();unsignedshorttemp0;floatvol0;charbuffer[128]{0};pwm1_init();adc1_init();lcd_init();screen_clear(0x00FFFFFF);//lcd_h_drawline(100, 100, 200, 0x00FF0000);//extern void lcd_show_string(unsigned short x,unsigned short y, unsigned short width, unsigned short height, unsigned char size,char *p);//lcd_show_string(200,200, 300, 32, 32, hello world!);while(1){tempget_adc_value();volget_voltage();intm0,n0;m(int)(vol*1000)/1000;n(int)(vol*1000)%1000;sprintf(buffer,adc %d, vol %d.%d\n,temp,m,n);lcd_show_string(100,100,500,32,32,buffer);delay_ms(10);}return0;}main函数说明外设初始化顺序系统中断 → 系统时钟 → LED、蜂鸣器、按键 → GPT定时器 → UART串口 → PWM1 → ADC1 → LCD然后清屏。主循环逻辑读取ADC原始(Voltage \frac{采样值}{4096} \times 3.3V)调用get_voltage计算电压浮点数把浮点数拆分为整数m和小数n方便LCD显示sprintf格式化字符串存入bufferlcd_show_string 在屏幕(100,100)位置打印数据延时10ms循环刷新三、实验现象调节电位器ADC原始(Voltage \frac{采样值}{4096} \times 3.3V)adc xxx实时跟着变化LCD屏幕实时刷新电压vol x.xxxPWM通道持续输出设定占空比的方波可以外接LED或者LCD背光引脚用来调节亮度。小结ADC自校准不能省略上电不校准采样结果不准甚至读取异常。PWM中断每次PWM周期完成触发中断在中断里面修改PWMSAR动态修改占空比。IM6ULL的PWM中断标志是写1清零不能读清零。LCD浮点数打印LCD没有标准printf使用sprintf把数值转为字符串再调用lcd_show_string显示。引脚复用IMX6ULL一个引脚有多个复用功能PWM输出引脚必须配置为PWM功能ADC引脚配置为模拟输入不能当成普通GPIO。GPIO与PWM对比GPIO只能高低电平开关PWM用于调光、电机调速ADC用来采集外部模拟量这两个外设是嵌入式模拟交互的基础。