ADC输入&PWMDAC亮度输出
由于总项目中涉及到ADC采集电压信息,因此必须调通ADC外设,而与ADC相对应的自然就是DAC了,在单片机中,非音频项目的DAC一般都用PWMDAC代替,也就是输出频率高且固定,占空比可变的PWM波,来实现改变LED灯亮度。
本次两个实验的外设都很好移植,为了节省时间,也因为项目中不需要用到PWM而只作为指示输出用,因此PWM输出的代码直接参考@一代睡神的崛起的帖子,不深究,着重分析ADC外设。
首先看看ADC的硬件连接,根据原理图可知,SDK默认的ADC引脚是H14(BGA编号),通过一个跳帽接到了一个精密电位器上面,如果是要看出明显效果的话当然不可能用精密电位器,应该选用平常使用的10K普通电位器,这种电位器也有3个接口,中间的接口连接ADC,两边的接口连接VCC和GND,由于板上没有输出VCC的2.54mm引脚,因此从UART2十针接口处取出5V电压:
<ignore_js_op> <ignore_js_op>
ADC外设选用SDK例程中的polling即循环阻塞采集例程,因为我的项目中ADC采集是循环进行的,为了不让CPU有太大的负担因此使用此例程,并将阻塞采集模式改成非阻塞轮询采集方式,下面再贴代码:
<ignore_js_op>
ADC初始化函数,直接使用SDK里面的默认参数进行初始化即可:
/*
* config->enableAsynchronousClockOutput = true;
* config->enableOverWrite = false;
* config->enableContinuousConversion = false;
* config->enableHighSpeed = false;
* config->enableLowPower = false;
* config->enableLongSample = false;
* config->referenceVoltageSource = kADC_ReferenceVoltageSourceVref;
* config->samplePeriodMode = kADC_SamplePeriod2or12Clocks;
* config->clockSource = kADC_ClockSourceAD;
* config->clockDriver = kADC_ClockDriver1;
* config->resolution = kADC_Resolution12Bit;
*/
adc_config_t adcConfigStrcut;
adc_channel_config_t adcChannelConfigStruct;
ADC_GetDefaultConfig(&adcConfigStrcut);
adcConfigStrcut.enableHighSpeed=true;
ADC_Init(ADC1, &adcConfigStrcut);
adcChannelConfigStruct.channelNumber = 3;
adcChannelConfigStruct.enableInterruptOnConversionCompleted = false;
启用ADC1的通道3,组别0,12位数据,直接使用内部AD时钟不分频,直接使用VREF参考电压,使用高低速模式看不出区别,就用高速模式。连续采集
模式应该跟多通道有关,遵循官方SDK配置为false,不乱动。复写模式也遵循配置不动。
采集时改为轮询采集,不占用CPU时间片等待:
while (1)
{
if(ADC_GetChannelStatusFlags(ADC1,0)==0)
{
ADC_SetChannelConfig(ADC1,0,&adcChannelConfigStruct);
printf("ADC Value: %d\r\n",ADC1->R[0]);
QTMER4CH3_PWM_DutySet(14,5000,ADC1->R[0]/40.95);
}
}
}
然后是PWM输出代码,直接参考@一代睡神的崛起的代码:
qtmr_config_t qtimer4pwm_config;
unsigned char Calcu_2invo(unsigned char time)
{
unsigned char i=0,value=1;
if(time>7)time=7;
if(time==0)
value=1;
else
{
for(i=0;i<time;i++)
{
value*=2;
}
}
return value;
}
void QTMR4_CH3_PWM_Init(unsigned char prisrc,int clk, unsigned char duty)
{
unsigned char fredivi=1;
qtmr_primary_count_source_t qtimer_source;
qtimer_source=(qtmr_primary_count_source_t)prisrc;
//配置GPIO_B1_11为QTIMER3_TIMER1的输出引脚
IOMUXC_SetPinMux(IOMUXC_GPIO_B1_11_QTIMER4_TIMER3,0);
//配置IO引脚GPIO_AD_B1_11的功能
//低转换速度,驱动能力为R0/6,速度为100Mhz,关闭开路功能,使能pull/keepr
//选择keeper功能,下拉100K Ohm,关闭Hyst
IOMUXC_SetPinConfig(IOMUXC_GPIO_B1_11_QTIMER4_TIMER3,0x10B0);
fredivi=Calcu_2invo(prisrc-8);
//初始化QTIMER4
QTMR_GetDefaultConfig(&qtimer4pwm_config); //先设置为默认配置,后面在根据实际情况配置
qtimer4pwm_config.primarySource=qtimer_source; //设置第一时钟源
QTMR_Init(TMR4,kQTMR_Channel_3,&qtimer4pwm_config); //初始化TIM4通道3
QTMR_SetupPwm(TMR4,kQTMR_Channel_3,clk,duty,false,CLOCK_GetFreq(kCLOCK_IpgClk)/fredivi); //初始化PWM
QTMR_StartTimer(TMR4,kQTMR_Channel_3,kQTMR_PriSrcRiseEdge); //通道3在primary时钟源的上升沿计数
}
void QTMER4CH3_PWM_DutySet(unsigned char prisrc,int clk, unsigned char duty)
{
unsigned char fredivi=1;
int srcclk,period,hightime,lowtime;
fredivi=Calcu_2invo(prisrc-8);
srcclk=CLOCK_GetFreq(kCLOCK_IpgClk)/fredivi;
period=(srcclk/clk); //一个PWM周期需要的计数值
hightime=(period*duty)/100; //一个PWM周期中高电平时间(计数值)
lowtime=period-hightime; //一个PWM周期中低电平时间(计数值)
TMR4->CHANNEL[kQTMR_Channel_3].CMPLD1=lowtime;
TMR4->CHANNEL[kQTMR_Channel_3].CMPLD2=hightime;
}
看看效果:
<ignore_js_op> <ignore_js_op>
本次两个实验的外设都很好移植,为了节省时间,也因为项目中不需要用到PWM而只作为指示输出用,因此PWM输出的代码直接参考@一代睡神的崛起的帖子,不深究,着重分析ADC外设。
首先看看ADC的硬件连接,根据原理图可知,SDK默认的ADC引脚是H14(BGA编号),通过一个跳帽接到了一个精密电位器上面,如果是要看出明显效果的话当然不可能用精密电位器,应该选用平常使用的10K普通电位器,这种电位器也有3个接口,中间的接口连接ADC,两边的接口连接VCC和GND,由于板上没有输出VCC的2.54mm引脚,因此从UART2十针接口处取出5V电压:
<ignore_js_op> <ignore_js_op>
ADC外设选用SDK例程中的polling即循环阻塞采集例程,因为我的项目中ADC采集是循环进行的,为了不让CPU有太大的负担因此使用此例程,并将阻塞采集模式改成非阻塞轮询采集方式,下面再贴代码:
<ignore_js_op>
ADC初始化函数,直接使用SDK里面的默认参数进行初始化即可:
/*
* config->enableAsynchronousClockOutput = true;
* config->enableOverWrite = false;
* config->enableContinuousConversion = false;
* config->enableHighSpeed = false;
* config->enableLowPower = false;
* config->enableLongSample = false;
* config->referenceVoltageSource = kADC_ReferenceVoltageSourceVref;
* config->samplePeriodMode = kADC_SamplePeriod2or12Clocks;
* config->clockSource = kADC_ClockSourceAD;
* config->clockDriver = kADC_ClockDriver1;
* config->resolution = kADC_Resolution12Bit;
*/
adc_config_t adcConfigStrcut;
adc_channel_config_t adcChannelConfigStruct;
ADC_GetDefaultConfig(&adcConfigStrcut);
adcConfigStrcut.enableHighSpeed=true;
ADC_Init(ADC1, &adcConfigStrcut);
adcChannelConfigStruct.channelNumber = 3;
adcChannelConfigStruct.enableInterruptOnConversionCompleted = false;
启用ADC1的通道3,组别0,12位数据,直接使用内部AD时钟不分频,直接使用VREF参考电压,使用高低速模式看不出区别,就用高速模式。连续采集
模式应该跟多通道有关,遵循官方SDK配置为false,不乱动。复写模式也遵循配置不动。
采集时改为轮询采集,不占用CPU时间片等待:
while (1)
{
if(ADC_GetChannelStatusFlags(ADC1,0)==0)
{
ADC_SetChannelConfig(ADC1,0,&adcChannelConfigStruct);
printf("ADC Value: %d\r\n",ADC1->R[0]);
QTMER4CH3_PWM_DutySet(14,5000,ADC1->R[0]/40.95);
}
}
}
然后是PWM输出代码,直接参考@一代睡神的崛起的代码:
qtmr_config_t qtimer4pwm_config;
unsigned char Calcu_2invo(unsigned char time)
{
unsigned char i=0,value=1;
if(time>7)time=7;
if(time==0)
value=1;
else
{
for(i=0;i<time;i++)
{
value*=2;
}
}
return value;
}
void QTMR4_CH3_PWM_Init(unsigned char prisrc,int clk, unsigned char duty)
{
unsigned char fredivi=1;
qtmr_primary_count_source_t qtimer_source;
qtimer_source=(qtmr_primary_count_source_t)prisrc;
//配置GPIO_B1_11为QTIMER3_TIMER1的输出引脚
IOMUXC_SetPinMux(IOMUXC_GPIO_B1_11_QTIMER4_TIMER3,0);
//配置IO引脚GPIO_AD_B1_11的功能
//低转换速度,驱动能力为R0/6,速度为100Mhz,关闭开路功能,使能pull/keepr
//选择keeper功能,下拉100K Ohm,关闭Hyst
IOMUXC_SetPinConfig(IOMUXC_GPIO_B1_11_QTIMER4_TIMER3,0x10B0);
fredivi=Calcu_2invo(prisrc-8);
//初始化QTIMER4
QTMR_GetDefaultConfig(&qtimer4pwm_config); //先设置为默认配置,后面在根据实际情况配置
qtimer4pwm_config.primarySource=qtimer_source; //设置第一时钟源
QTMR_Init(TMR4,kQTMR_Channel_3,&qtimer4pwm_config); //初始化TIM4通道3
QTMR_SetupPwm(TMR4,kQTMR_Channel_3,clk,duty,false,CLOCK_GetFreq(kCLOCK_IpgClk)/fredivi); //初始化PWM
QTMR_StartTimer(TMR4,kQTMR_Channel_3,kQTMR_PriSrcRiseEdge); //通道3在primary时钟源的上升沿计数
}
void QTMER4CH3_PWM_DutySet(unsigned char prisrc,int clk, unsigned char duty)
{
unsigned char fredivi=1;
int srcclk,period,hightime,lowtime;
fredivi=Calcu_2invo(prisrc-8);
srcclk=CLOCK_GetFreq(kCLOCK_IpgClk)/fredivi;
period=(srcclk/clk); //一个PWM周期需要的计数值
hightime=(period*duty)/100; //一个PWM周期中高电平时间(计数值)
lowtime=period-hightime; //一个PWM周期中低电平时间(计数值)
TMR4->CHANNEL[kQTMR_Channel_3].CMPLD1=lowtime;
TMR4->CHANNEL[kQTMR_Channel_3].CMPLD2=hightime;
}
看看效果:
<ignore_js_op> <ignore_js_op>