本帖最后由 zhangbaoyin 于 2023-6-19 11:50 编辑
【雅特力AT-START-F435】ADC软件触发always、DACTmr2触发、DMA,驱动LED被钳位1.54V问题
目的:
- 使用PB0采样电压,采样结束产生DMA请求,DMA将数据搬移到变量adc1_ordinary_valuetab中
- 使用PA4输出采样电压,杜邦线连接到PD13驱动LED2,实现电压随动
解决的问题:
- 使用Demo,直接从ADC搬移的数值放入DAC寄存器数值错误
- DMA的8bit数据寄存器和12bit数据寄存器不相同,小心搬移错误
- 不开启DxOBDIS输出增益,会导致驱动LED最低1.54V问题
一、初始化ADC
/**
* [url=home.php?mod=space&uid=159083]@brief[/url] PB0 ADC-GPIO configuration.
*
*/
void gpio_config(void)
{
gpio_init_type gpio_initstructure;
crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
gpio_default_para_init(&gpio_initstructure);
/* config adc pin as analog input mode */
gpio_initstructure.gpio_mode = GPIO_MODE_ANALOG;
gpio_initstructure.gpio_pins = GPIO_PINS_0;
gpio_init(GPIOB, &gpio_initstructure);
}
- 使用DMA1-CH1,开启DMA1-CH1的NVIC中断(DMA1_Channel1_IRQn),用于记录DMA搬移成功的次数,也就是计算采样率(具体计算采样率的代码在下一节)
- buffer_size:DMA的一次搬移数据个数,就一个ADC通道,采用一个数据就好
- direction:由ADC到内存,为DMA_DIR_PERIPHERAL_TO_MEMORY(外设到内存)
- memory_base_addr为内存地址,设置为一个数组就好,然后传入数组首地址,并将地址转化为 (uint32_t) 类型,而不是 (* uint32_t) 的指针类型
- 因为使用的是12bitADC采样,所以DMA传输宽度使用WIDTH_HALFWORD(16bit半字宽度)
- 外设和内存自动地址增加设置为FALSE
- 将ADC通道的DMA的优先级设置为高(DAC设置为中,以ADC为主,DCA为次)
/**
* @brief dma configuration.
* @param none
* @retval none
*/
void dma_config(void)
{
dma_init_type dma_init_struct;
crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);
nvic_irq_enable(DMA1_Channel1_IRQn, 0, 0);
dma_reset(DMA1_CHANNEL1);
dma_default_para_init(&dma_init_struct);
dma_init_struct.buffer_size = 1;
dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
dma_init_struct.memory_base_addr = (uint32_t)adc1_ordinary_valuetab;
dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_HALFWORD;
dma_init_struct.memory_inc_enable = FALSE;
dma_init_struct.peripheral_base_addr = (uint32_t)&(ADC1->odt);
dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_HALFWORD;
dma_init_struct.peripheral_inc_enable = FALSE;
dma_init_struct.priority = DMA_PRIORITY_HIGH;
dma_init_struct.loop_mode_enable = TRUE;
dma_init(DMA1_CHANNEL1, &dma_init_struct);
dmamux_enable(DMA1, TRUE);
dmamux_init(DMA1MUX_CHANNEL1, DMAMUX_DMAREQ_ID_ADC1);
/* enable dma transfer complete interrupt */
dma_interrupt_enable(DMA1_CHANNEL1, DMA_FDT_INT, TRUE);
dma_channel_enable(DMA1_CHANNEL1, TRUE);
}
- ADC模式设置为独立模式 ADC_INDEPENDENT_MODE
- ADC挂载到HCLK(和CPU同频率),为了使ADC采样速度最大,采用2分频 ADC_HCLK_DIV_2
- common_dma_mode不用配置,在独立模式不起作用
- ADC sequence_mode设置为FALSE,不使用序列模式(因为就使用了一个ADC通道)
- ADC repeat_mode设置为TRUE,重复模式,就触发一次,然后一直采样
- ADC data_align 对齐模式选择为右对齐
- ADC ordinary_channel_length 通道个数选择为1个
- 配置这个通道的采样顺序,采样时间为92.5个ADC时钟周期
- 选择数据寄存器的对齐模式为右对齐
- 使能ADC
- 使能ADC的DMA请求
- 开启ADC
- 开启ADC的自校准,并等待校准完成
/**
* @brief adc configuration.
* @param none
* @retval none
*/
void adc_config(void)
{
adc_common_config_type adc_common_struct;
adc_base_config_type adc_base_struct;
crm_periph_clock_enable(CRM_ADC1_PERIPH_CLOCK, TRUE);
nvic_irq_enable(ADC1_2_3_IRQn, 0, 0);
adc_common_default_para_init(&adc_common_struct);
/* config combine mode */
adc_common_struct.combine_mode = ADC_INDEPENDENT_MODE;
/* config division,adcclk is division by hclk */
adc_common_struct.div = ADC_HCLK_DIV_2;
/* config common dma mode,it's not useful in independent mode */
adc_common_struct.common_dma_mode = ADC_COMMON_DMAMODE_DISABLE;
/* config common dma request repeat */
adc_common_struct.common_dma_request_repeat_state = FALSE;
/* config adjacent adc sampling interval,it's useful for ordinary shifting mode */
adc_common_struct.sampling_interval = ADC_SAMPLING_INTERVAL_5CYCLES;
/* config inner temperature sensor and vintrv */
adc_common_struct.tempervintrv_state = FALSE;
/* config voltage battery */
adc_common_struct.vbat_state = FALSE;
adc_common_config(&adc_common_struct);
adc_base_default_para_init(&adc_base_struct);
adc_base_struct.sequence_mode = TRUE;
adc_base_struct.repeat_mode = TRUE;
adc_base_struct.data_align = ADC_RIGHT_ALIGNMENT;
adc_base_struct.ordinary_channel_length = 1;
adc_base_config(ADC1, &adc_base_struct);
adc_resolution_set(ADC1, ADC_RESOLUTION_12B);
/* config ordinary channel */
adc_ordinary_channel_set(ADC1, ADC_CHANNEL_8, 1, ADC_SAMPLETIME_92_5);
/* config ordinary trigger source and trigger edge */
adc_ordinary_conversion_trigger_set(ADC1, ADC_ORDINARY_TRIG_TMR1CH1, ADC_ORDINARY_TRIG_EDGE_NONE);
/* config dma mode,it's not useful when common dma mode is use */
adc_dma_mode_enable(ADC1, TRUE);
/* config dma request repeat,it's not useful when common dma mode is use */
adc_dma_request_repeat_enable(ADC1, TRUE);
/* enable adc overflow interrupt */
adc_interrupt_enable(ADC1, ADC_OCCO_INT, TRUE);
/* adc enable */
adc_enable(ADC1, TRUE);
while(adc_flag_get(ADC1, ADC_RDY_FLAG) == RESET);
/* adc calibration */
adc_calibration_init(ADC1);
while(adc_calibration_init_status_get(ADC1));
adc_calibration_start(ADC1);
while(adc_calibration_status_get(ADC1));
}
二、初始化DAC
- 设置为模拟模式
- 因为要驱动外设,所以驱动能力选择为强驱动模式GPIO_DRIVE_STRENGTH_STRONGER
- 设置为向上计数模式
- 设置计数上限为999(从0开始计数到999经历1000个时钟周期)
- TMR2的时钟频率设置为:(systemclock/(systemclock/1000000))/1000 = 1KHz
- 模式选择为 TMR_PRIMARY_SEL_OVERFLOW:tmr primary mode select overflow
- DAC1触发选择TMR2 DAC_TMR2_TRGOUT_EVENT
- 使能DAC2触发模式
- 不启用噪声波
- 使能输出增益( dac_output_buffer_enable(DAC1_SELECT, TRUE) ),这个是必须的,不然LED无法驱动
- 使能DAC(开启DAC)
- 选择DMA通道为DMA1-CH2
- buffer_size:DMA的一次搬移数据个数,就一个DAC通道,采用一个数据就好
- direction:由ADC到内存,为DMA_DIR_MEMORY_TO_PERIPHERAL(内存到外设)
- memory_base_addr为内存地址,设置为一个数组就好,然后传入数组首地址,并将地址转化为 (uint32_t) 类型,而不是 (* uint32_t) 的指针类型
- peripheral_base_addr为外设地址:(DAC_BASE+0x008) // DAC1的12位右对齐数据保持寄存器(DAC_D1DTH12R),偏移地址008H
- 因为使用的是12bitADC采样,所以DMA传输宽度使用WIDTH_HALFWORD(16bit半字宽度)
- 外设和内存自动地址增加设置为FALSE
- 将DAC通道的DMA的优先级设置为中(DAC设置为中,以ADC为主,DCA为次)
void DAC_Config(void)
{
gpio_init_type gpio_init_struct = {0};
dma_init_type dma_init_struct;
crm_clocks_freq_type crm_clocks_freq_struct = {0};
crm_periph_clock_enable(CRM_TMR2_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_DAC_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
/* once the dac is enabled, the corresponding gpio pin is automatically
connected to the dac converter. in order to avoid parasitic consumption,
the gpio pin should be configured in analog */
gpio_init_struct.gpio_pins = GPIO_PINS_4;
gpio_init_struct.gpio_mode = GPIO_MODE_ANALOG;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init(GPIOA, &gpio_init_struct);
/* get system clock */
crm_clocks_freq_get(&crm_clocks_freq_struct);
/* (systemclock/(systemclock/1000000))/1000 = 1KHz */
tmr_base_init(TMR2, 999, (crm_clocks_freq_struct.sclk_freq/1000000 - 1));
tmr_cnt_dir_set(TMR2, TMR_COUNT_UP);
/* primary tmr2 output selection */
tmr_primary_mode_select(TMR2, TMR_PRIMARY_SEL_OVERFLOW);
/* dac1 configuration */
dac_trigger_select(DAC1_SELECT, DAC_TMR2_TRGOUT_EVENT);
dac_trigger_enable(DAC1_SELECT, TRUE);
dac_wave_generate(DAC1_SELECT, DAC_WAVE_GENERATE_NONE);
dac_output_buffer_enable(DAC1_SELECT, TRUE);
dac_dma_enable(DAC1_SELECT, TRUE);
/* dma1 channel2 configuration */
dma_reset(DMA1_CHANNEL2);
dma_init_struct.buffer_size = 1;
dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_base_addr = (uint32_t)adc1_ordinary_valuetab;
dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_HALFWORD;
dma_init_struct.memory_inc_enable = FALSE;
dma_init_struct.peripheral_base_addr = (uint32_t)(DAC_BASE+0x008);//0x40007410
dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_HALFWORD;
dma_init_struct.peripheral_inc_enable = FALSE;
dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
dma_init_struct.loop_mode_enable = TRUE;
dma_init(DMA1_CHANNEL2, &dma_init_struct);
/* enable dmamux function */
dmamux_enable(DMA1, TRUE);
dmamux_init(DMA1MUX_CHANNEL2, DMAMUX_DMAREQ_ID_DAC1);
dma_channel_enable(DMA1_CHANNEL2, TRUE);
/* enable dac1: once the dac1 is enabled, pa.04 is
automatically connected to the dac converter. */
dac_enable(DAC1_SELECT, TRUE);
/* enable tmr2 */
tmr_counter_enable(TMR2, TRUE);
}
三、实验效果
73eaab0fe346fc91257936516f12e7b1
如果不开启输出增益D1OBDIS使能,DAC的GPIO驱动能力比LED端口的驱动能力弱,无法抵抗LED的稳压特性,最低电压被钳位在稳压的1.54v。
所以如果要驱动外设,一点要开启输出增益。