打印
[APM32F4]

APM32F411 TINY板实现TMR触发ADC采样+DMA传输

[复制链接]
699|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
DKENNY|  楼主 | 2023-9-16 13:49 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 DKENNY 于 2023-9-18 14:05 编辑

最近看了看ADC采样,想着把ADC和DMA以及TMR结合起来研究看看,达到使用定时器触发ADC采样,并使用DMA传输的功能,最近正好收到了APM32F411开发板,就想着使用这款开发板实现一下这个功能。

1. APM32F411简介
-带有FPU的32位ARM CortexM4F内核
-支持最高120MHz的工作频率
-具有512KB+NVR 32KB的flash容量,128KB的SRAM容量
-支持睡眠、停机、待机三种工作模式
-支持CF卡 、SRAM、PSRAM、NOR和NAND存储器
-具有多个通信外设以及模拟外设
更多详细资料可以访问他们的官网(https://geehy.com)。

2. APM32F411系统工作主频
在调试时,我发现这款芯片的官方SDK里面,芯片的运行主频不是120MHz,而是使用的是100MHz。当然,这个主频我们是可以直接算出来的。我们进入Debug视图,运行到main函数,打开RCM模块视图。


我们可以看到PLL1CFG寄存器中的各项值,这时,就可以参考官方手册给出的计算公式,计算出它的主频。

通过查看官方用户手册,上面有关于这部分的详细阐述,最后算得系统实际工作主频为8M*(100/4)/2= 100MHz。这对后面我配置TMR2定时触发是有一定影响的,所以在这里提一下。

3. 代码
1、定时器
这里我选择TMR2定时器的时钟更新事件触发ADC采样,也就是TMR的重装载寄存器置位1次,则触发一次ADC数据采集。
/*
    定时器初始化
*/
void TMR_Init(void)
{
    TMR_BaseConfig_T timBaseConfig;
   
    //系统工作主频为100M,由于APB1是2分频,所以时基计算如下
    timBaseConfig.period = 3; //设置定时器时基为4us,(3+1)*(99+1)/(50M*2) = 4us
    timBaseConfig.division = 99;
    timBaseConfig.clockDivision = TMR_CLOCK_DIV_1;
    timBaseConfig.countMode = TMR_COUNTER_MODE_UP;
    TMR_ConfigTimeBase(TMR2,&timBaseConfig);

    //选择外部触发源
    TMR_SelectOutputTrigger(TMR2, TMR_TRGO_SOURCE_UPDATE);
  
    TMR_EnableInterrupt(TMR2,TMR_INT_UPDATE);
    NVIC_EnableIRQRequest(TMR2_IRQn,0,0);

    TMR_Enable(TMR2);
}

/*
    TMR2中断服务函数
*/
void TMR2_IRQHandler(void)
{
    if (TMR_ReadIntFlag(TMR2, TMR_INT_UPDATE) != RESET)
    {
        TMR_ClearIntFlag(TMR2, TMR_INT_UPDATE);

        if(count)
        {
            count--;
        }
        else
        {
              //定时1s,打印一次数据
             count = 250000;
              if(DMA_ReadCurrentMemoryTarget(DMA2_Stream0))
              {
                  printf("The memory currently accessed by DMA : 1 \r\n");
                  printf("Volatage = %.2f V\r\n",(double)adc_data[1][0]/4096*3.3);
              }
              else
              {
                  printf("The memory currently accessed by DMA : 0 \r\n");
                  printf("Volatage = %.2f V\r\n",(double)adc_data[0][0]/4096*3.3);
               }
                APM_TINY_LEDToggle(LED2);
         }
    }
}

2、ADC
APM32F411有2个ADC源,我使用的是ADC1,并且设置的是TMR2定时器触发,我们可以看看官方手册对于ADC外部触发的描述。
/**
    ADC初始化代码
*/
void ADC_Init(void)
{
    ADC_Config_T adcConfig;
    ADC_CommonConfig_T adcCommonConfig;

    ADC_Reset();

    /*
        ADC采样:50M,(112+12)/50M = 2.48us,2.48us进行一次ADC采样
    */
    adcCommonConfig.accessMode = ADC_ACCESS_MODE_DISABLED;//ADC不采用直接访问DMA模式
    adcCommonConfig.mode = ADC_MODE_INDEPENDENT;     //ADC设置为独立模式
    adcCommonConfig.prescaler = ADC_PRESCALER_DIV2; //ADC 2分频:100M/2=50M
    adcCommonConfig.twoSampling = ADC_TWO_SAMPLING_5CYCLES;        //2个采样阶段之间的延迟:只在双重或三重交错模式下使用
    ADC_CommonConfig(&adcCommonConfig);

    adcConfig.resolution = ADC_RESOLUTION_12BIT;//设置ADC分辨率
    adcConfig.scanConvMode = DISABLE;//不使能ADC循环扫描模式
    adcConfig.continuousConvMode = DISABLE;//设置ADC单次转换模式,否则除了第一次是由外部TMR触发以外,其他的都是软件触发了
    adcConfig.extTrigEdge = ADC_EXT_TRIG_EDGE_RISING; //设置ADC外部上升沿触发
    adcConfig.extTrigConv = ADC_EXT_TRIG_CONV_TMR2_TRGO;//设置ADC使用TMR2触发
    adcConfig.dataAlign = ADC_DATA_ALIGN_RIGHT; //ADC数据右对齐
    adcConfig.nbrOfChannel = 1; //设置ADC转换通道个数
    ADC_Config(ADC1, &adcConfig);

    ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_112CYCLES);
    ADC_EnableDMA(ADC1);
    ADC_EnableDMARequest(ADC1);

    ADC_Enable(ADC1);
}

3、DMA
我这里只用了一路ADC一个通道数据传输到DMA,也可以ADC多通道传输,只需要稍微改一下DMA程序并定义几个空间数据用来存放数据即可。
/*DMA初始化*/
void DMA_Configuration(void)
{
    DMA_Config_T dmaConfig;

    DMA_Reset(DMA2_Stream0);
    dmaConfig.channel = DMA_CHANNEL_0;
    dmaConfig.peripheralBaseAddr = (uint32_t) & (ADC1->REGDATA);//配置DMA的外设基地址为ADC1数据寄存器
    dmaConfig.memoryBaseAddr = (uint32_t)adc_data[0];//配置DMA的主存基地址
    dmaConfig.dir = DMA_DIR_PERIPHERALTOMEMORY;//配置DMA传输方向:外设->主存
    dmaConfig.bufferSize = 1;
    dmaConfig.peripheralInc = DMA_PERIPHERAL_INC_DISABLE;//DMA外设基地址不自增
    dmaConfig.memoryInc = DMA_MEMORY_INC_ENABLE;//DMA主存基地址自增
    dmaConfig.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_HALFWORD;//DMA以半字为单位进行传输
    dmaConfig.memoryDataSize = DMA_MEMORY_DATA_SIZE_HALFWORD;
    dmaConfig.loopMode = DMA_MODE_NORMAL;//设置DMA为NORMAL模式,也就是说,当外部TMR触发ADC采样时,DMA开始传输
    dmaConfig.priority = DMA_PRIORITY_HIGH;
    dmaConfig.fifoMode = DMA_FIFOMODE_DISABLE;
    dmaConfig.fifoThreshold = DMA_FIFOTHRESHOLD_HALFFULL;
    dmaConfig.memoryBurst = DMA_MEMORYBURST_SINGLE;
    dmaConfig.peripheralBurst = DMA_PERIPHERALBURST_SINGLE;
    DMA_Config(DMA2_Stream0, &dmaConfig);

    //开启DMA双缓冲模式
    DMA_ConfigBufferMode(DMA2_Stream0,(uint32_t)adc_data[1],DMA_MEMORY_0);
    DMA_EnableDoubleBufferMode(DMA2_Stream0);
    DMA_Enable(DMA2_Stream0);

    //使能DMA完成中断
    DMA_EnableInterrupt(DMA2_Stream0,DMA_INT_TCI**);
    NVIC_EnableIRQRequest(DMA2_STR0_IRQn,0,0);
}

/*
    DMA_Stream0中断服务函数
*/
void DMA2_STR0_IRQHandler(void)
{
    if(DMA_ReadIntFlag(DMA2_Stream0,DMA_INT_TCI**0) != RESET)
    {
        DMA_ClearIntFlag(DMA2_Stream0,DMA_INT_TCI**0);
    }
}

4、main函数
volatile uint32_t adc_data[2][1]; // 存储ADC采集的数据

void RCM_Init(void);
void GPIO_Init(void);
void TMR_Init(void);
void ADC_Init(void);
void DMA_Configuration(void);

volatile uint32_t count = 250000;

/*
    时钟初始化
*/
void RCM_Init(void)
{
    RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_DMA2 | RCM_AHB1_PERIPH_GPIOA);
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1);
    RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR2);
}

/*
    GPIO初始化
*/
void GPIO_Init(void)
{
    GPIO_Config_T gpioConfig;

    gpioConfig.pin = GPIO_PIN_0;
    gpioConfig.mode = GPIO_MODE_AN;
    gpioConfig.pupd = GPIO_PUPD_NOPULL;
    GPIO_Config(GPIOA, &gpioConfig);
}


int main(void)
{
    USART_Config_T usartConfig;
   
    usartConfig.baudRate = 115200;
    usartConfig.hardwareFlow = USART_HARDWARE_FLOW_NONE;
    usartConfig.mode = USART_MODE_TX_RX;
    usartConfig.parity = USART_PARITY_NONE;
    usartConfig.stopBits = USART_STOP_BIT_1;
    usartConfig.wordLength = USART_WORD_LEN_8B;
   
    USART_Config(USART1,&usartConfig);
    APM_TINY_COMInit(COM1,&usartConfig);
    APM_TINY_LEDInit(LED2);

    RCM_Init();
    GPIO_Init();
    TMR_Init();
    ADC_Init();
    DMA_Configuration();

    while (1)
    {
    }
}



4. 运行效果


5. 例程源码
DMA_TMR2_ADC.zip (734.51 KB)


欢迎大家讨论交流。





使用特权

评论回复
评论
kai迪皮 2023-9-22 10:58 回复TA
不错不错,学习了 
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

35

主题

61

帖子

6

粉丝