CKS32F107xx

定时器同步

CKS32F107xx系列部分定时器在内部是相连的,可用于定时器同步或链接,方便用户配置不同的同步模式,以便在电机控制、数据采集和PWM信号生成等应用中,实现复杂的时间序列和多通道的同步操作。本节课我们将围绕定时器同步功能概述展开,并以其中一种功能为例——将定时器2给定时器3当预分频器,最终级联同步PWM输出。

依据《CKS32F107xx参考手册》的从模式控制寄存器相关章节得知,并非任意两个定时器都能任意级联,硬件方面是固定的,若某个产品中没有相应的定时器,则对应的触发信号ITRx也不存在。如下表1所示。

1.png

表1 普通输入捕获概览图

如下图1所示,TIM2更新事件的输出信号TRGO1可以连接到TIM3的内部触发信号线ITR1(可由表1查阅得知),作为TIM3 的时钟输入。

2.png

图1 主从定时器示例

要使TIM2和TIM3级联同步输出PWM波形,我们还会用到控制寄存器2(TIMx_CR2)和从模式控制寄存器(TIMx_SMCR)。接下来我们简单介绍下这两个寄存器。

首先是控制寄存器2(TIMx_CR2),该寄存器的各位描述如下图所示:

3.png

4.png

图2 TIMx_CR2

关于该寄存器的详细说明,请参照《CKS32F107xx参考手册》。这里我们用到的是主模式选择位域MMS,此部分由3位组成。共有8种模式可供选择,因为我们使用的是更新模式,所以必须设置为010。

接下来我们介绍从模式控制寄存器(TIMx_SMCR),该寄存器的各位描述如下图所示:

5.png

6.png

 图3 TIMx_SMCR

在该寄存器中,我们用到了SMS和TS位域,均由3位组成,各有8种不同模式组合。其中SMS控制从模式选择,这里我们配置成100,选择门控模式,确保从定时器的开始和结束都是由主定时器的输出信号控制。TS控制触发选择,根据表1描述得知,我们必须配置成001。

定时器级联同步输出PWM配置操作

通过上述对定时器功能的描述,下面我们要实现TIM2发生事件更新时发送触发信号驱动TIM3计数,并使能两个定时器在PWM1模式下由定时器通道1输出PWM波形。编程的要点如下所示。

1、配置PA0和PA6引脚为复用输出功能

我们调用如下函数实现:

void TIM_GPIO_Configuration(void)

{

  GPIO_InitTypeDef GPIO_InitStructure;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);


  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_6;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOA, &GPIO_InitStructure);

}

2、TIM2配置为主定时器

TIM2配置为PWM1模式输出,使能主从模式,并选择更新事件作为触发输出,主要代码如下:

void TIM2_Master_Mode_Configuration(void)

{

  TIM_TimeBaseInitTypeDef      TIM2_TimeBaseStructure;

TIM_OCInitTypeDef           TIM2_OCInitStructure;

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);


/* Time Base Configuration */

  TIM2_TimeBaseStructure.TIM_Prescaler = 8;

  TIM2_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

  TIM2_TimeBaseStructure.TIM_Period = 79;

  TIM2_TimeBaseStructure.TIM_ClockDivision = 0;

  TIM_TimeBaseInit(TIM2, &TIM2_TimeBaseStructure);


/* TIM2 Channel 1 Configuration in PWM1 mode */

  TIM2_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

  TIM2_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

  TIM2_OCInitStructure.TIM_Pulse = 40 ;

  TIM2_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

  TIM_OC1Init(TIM2, &TIM2_OCInitStructure);

  TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);


/* Master Mode selection */

  TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);

}

从上述代码可得知,TIM2CLK固定为72MHz,TIM2预分频为8,因此主定时器TIM2计数器时钟频率为8MHz频率,则TIM2频率=TIM2计数器时钟/(TIM2_Period + 1)=100KHz,占空比为TIM2_CCR1/(TIM2_ARR + 1) = 50%。

3、TIM3配置为从定时器

TIM3配置为门控模式,并选择ITR1作为输入,主要代码如下:

void TIM3_Slave_Mode_Configuration(void)
{  
    TIM_TimeBaseInitTypeDef  TIM3_TimeBaseStructure;  
    TIM_OCInitTypeDef      TIM3_OCInitStructure;  
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);  
    /* Time Base Configuration */  
    TIM3_TimeBaseStructure.TIM_Prescaler = 0;  
    TIM3_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
    TIM3_TimeBaseStructure.TIM_Period = 4;  
    TIM3_TimeBaseStructure.TIM_ClockDivision = 0;  
    TIM_TimeBaseInit(TIM3, &TIM3_TimeBaseStructure); 
    /* TIM3 Channel 1 Configuration in PWM1 mode */  
    TIM3_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;  
    TIM3_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  
    TIM3_OCInitStructure.TIM_Pulse = 2;  
    TIM3_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  
    TIM_OC1Init(TIM3, &TIM3_OCInitStructure);  
    /* Slave Mode selection: TIM3 */  
    TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Gated);  
    TIM_SelectInputTrigger(TIM3, TIM_TS_ITR1);
}

从上述代码可得知,从定时器TIM3频率为(TIM2 frequency)/ (TIM3 period + 1)=20kHz,占空比为TIM3_CCR1/(TIM3_ARR + 1) = 40%。

4、主函数

初始化主从定时器,最终实现级联同步PWM输出,主要代码如下:

int main(void)

{

  TIM_GPIO_Configuration();

  TIM3_Slave_Mode_Configuration();

  TIM2_Master_Mode_Configuration();

  TIM_Cmd(TIM2, ENABLE); /* Enable TIM2 Counter */

  TIM_Cmd(TIM3, ENABLE);  /* Enable TIM3 Counter */


  while(1)

  {

  }

}

至此,用示波器测量PA0和PA6引脚的波形(注意共地);下载程序,调节示波器,可在示波器看到有PWM波形输出,如下图4所示:

7.png


图4 PWM波形输出

相关阅读:

MCU微课堂|CKS32F107xx TIM(一)

MCU微课堂 | CKS32F107xx TIM(二)

MCU微课堂 | CKS32F107xx TIM(三)

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 5

相关阅读:

MCU微课堂|CKS32F107xx TIM(一)

MCU微课堂 | CKS32F107xx TIM(二)

PWM输入捕获

上节课我们介绍了高级定时器互补PWM输出的配置方法,这节课我们将向大家介绍高级定时器的另一个常见应用——PWM输入模式。在本节课中,我们将先围绕输入捕获模式展开,并重点描述PWM输入模式和涉及的寄存器,最后通过一个实验例程去介绍PWM输入模式的配置方法。

输入捕获模式简介

1、普通输入模式

除基本定时器外,其余定时器的部分通道都可以对输入信号的上升沿、下降沿或者双边沿进行捕获,并且计数器CNT的值会被锁存到捕获/比较寄存器CCR中。当我们需要同时捕获PWM波的频率和占空比时,仅需要测出一个周期中高电平和低电平持续的时间即可。我们可尝试如下步骤:首先将定时器的某一通道初始设置为上升沿捕获,当发生第一次捕获到上升沿的中断,以此中断时刻作为一个起点,读取CCR中值为Value1,此时将捕获模式设置为下降沿捕获,在发生第二次中断的时候,捕获到了下降沿,读取CCR中值为Value2,那么Value2和Value1之间的差值,就是一个周期中高电平持续的时间,然后我们在中断中又将捕获的方式设置为上升沿捕获,那么在第三次产生中断的时候,读取CCR中值为Value3,那么Value3和Value2之间的差值就是一个周期中低电平的时间。至此,PWM波的频率可由Value3和Value1之间的差值计算出,正占空比则可通过高电平占整个周期的比值获得。如下图1所示:

1.png

图1 普通输入捕获概览图

2、PWM输入模式

PWM输入模式是普通输入模式的一种特例,以输入通道TI1为例,PWM信号进入该通道后,信号被分成TI1FP1和TI1FP2两路,最终分别映射到了IC1和IC2捕获通道,其中一个捕获上升沿,另一个捕获下降沿。这样用户可以在中断中去读上升沿和下降沿对应寄存器中的计数,从而得出周期和占空比。并且用户在设计之初,需要先选定哪一路为触发信号以及触发极性,一旦选定某一路为触发信号则对应的即为周期,另一路则对应为占空比,两路捕获极性也是相反的,并且因为是PWM输入捕获的缘故,当其中一路配置完成,另一路由硬件自动配置,无需软件来配置。图2为PWM输入模式时序图。

2.png

图2 PWM输入模式时序图

其中,IC1捕获通道计算两次都是上升沿的时间,即周期T;而IC2通道则计算一次下降沿和之前上升沿之差,这样得到高电平时长,从而可以求得周期T和占空比。需要注意的是,PWM输入模式需要占用两个捕获寄存器,且只有TI1FP1和TI2FP2连接到了从模式控制器(使用PWM输入捕获时,需要配置从模式控制器为复位模式),所以只能使用定时器的通道1或通道2。

寄存器和输入捕获结构体概述

1、捕获/比较寄存器CCMR

关于CCMR寄存器,上节课我们介绍了输出比较模式,这节课我们来介绍下输入捕获模式,该寄存器的各位描述图如下:

3.png

图3 CCMR1寄存器各位描述图

该16位寄存器CCMR的下层对应输入捕获(上层对应输出比较),其中CCMR1用于捕获通道1和2的控制,CCMR2用于捕获通道3和4的控制。下图为低8位详细描述图,用于捕获通道1。

4.png

图4 CCMR1寄存器低7位描述图

参数CC1S,用于输入捕获/输出比较通道的引脚选择,若我们设置CCIS[1:0] = 01,表明CC1通道(对应定时器的通道1)被配置为输入,IC1映射在TI1上。

参数IC1PSC,配置为00时,表明每1个边沿触发1次捕获。

参数IC1F,用来设置TI1输入采样频率和数字滤波器长度,本课中我们不做滤波处理。

2、捕获/比较使能寄存器CCER

在本课中我们仅用到低2位CC1E和CC1P,由于我们需要在中断中处理捕获的数据,所以配置CC1E为1,CC1P配置为不反相,故设置为0。CCER低2位图如下描述。 5.png

图5 CCMR1寄存器低7位描述图

3、输入捕获结构体TIM_ICInitTypeDef

可配合TIM_PWMIConfig函数完成定时器输入通道各参数的初始化配置。输入捕获结构体的各参数定义如下:

typedef struct

{

uint16_t TIM_Channel;
uint16_t TIM_ICPolarity;
uint16_t TIM_ICSelection;
uint16_t TIM_ICPrescaler;
uint16_t TIM_ICFilter; 

} TIM_ICInitTypeDef;

1)参数TIM_Channel:设定CCMRx寄存器CCxS位,用于捕获通道ICx选择。

2)参数TIM_ICPolarity:设定CCER寄存器CCxP位和CCxNP位,用于输入捕获边沿触发选择。

3)参数TIM_ICSelection:设定CCRMx寄存器的CCxS[1:0]位,用于输入通道选择,输入通道共有三个来源,分别为:TIM_ICSelection_DirectTI、TIM_ICSelection_IndirectTI或TIM_ICSelection_TRC。若为普通输入模式,4个通道均能使用;若为PWM输入模式,只能使用通道1和2。输入通道和捕获通道的映射关系详见下图。

6.png

图6 输入通道和捕获通道的映射关系

4)参数TIM_ ICPrescaler:设定CCMRx寄存器的ICxPSC[1:0]位的值,用来设置输入捕获分频系数,有1、2、4、8分频可选。这里我们需要捕获输入信号的每个有效边沿,故设置为1分频即可。

5)参数TIM_ ICFilter:设定CCMRx寄存器ICxF[3:0]位,用于设置输入捕获滤波器。

本课中我们配置的示例代码如下:

TIM_ICInitTypeDef        TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_PWMIConfig(TIM1, &TIM_ICInitStructure);

上述代码中,我们设置定时器的通道1为上升沿捕获,且输入通道1(TI1)与捕获通道1(IC1)为直接映射,不分频,不使用滤波器。 

PWM模式输入配置实验

本实验配置高级定时器的通道1,即PA8,用于捕获信号发生器输出PWM信号,最后通过串口调试助手打印捕获到的PWM的频率和占空比。主要的编程要点如下。

1、高级定时器引脚初始化

由于TIM1_CH1是连接在PA8上,这里需要开启GPIOA时钟,并配置引脚为浮空输入,我们配置的代码如下:

void TIM1_GPIO_Configuration(void)
{  
    GPIO_InitTypeDef      GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);      
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

2、高级定时器中断优先级设置

之前的课程中让我们对中断优先级已经有了深入了解,这里因为我们只有一个捕获/比较中断源,所以优先级随便设置,我们配置的代码如下:

void TIM1_NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);  
    NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

3、高级定时器PWM输入模式配置

我们主要对时基和输入捕获结构体初始化,配置代码如下:

void TIM1_Input_Capture_Mode_Configuration(void)

{

TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

TIM_ICInitTypeDef        TIM_ICInitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);


     /* Time Base Configuration */

TIM_TimeBaseStructure.TIM_Period = 65535-1;

TIM_TimeBaseStructure.TIM_Prescaler = 72-1;

TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;  

TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;  

TIM_TimeBaseStructure.TIM_RepetitionCounter=0;

TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);


/* PWM Input Capture Configuration */

    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;

    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;

    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;

    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;

    TIM_ICInitStructure.TIM_ICFilter = 0x0;

    TIM_PWMIConfig(TIM1, &TIM_ICInitStructure);


TIM_SelectInputTrigger(TIM1, TIM_TS_TI1FP1);  

    TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Reset);

    TIM_SelectMasterSlaveMode(TIM1,TIM_MasterSlaveMode_Enable);

    TIM_ITConfig(TIM1, TIM_IT_CC1, ENABLE);

TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);

    TIM_Cmd(TIM1, ENABLE);

}

从上述示例代码来看,我们首先将TIM1_CH1的捕获计数器设置为1us计数一次,重装载值为65535,所以可以捕获的时间精度为1us,最低可捕获的频率为15.3Hz。其次,我们选择TIM1的通道1(TI1)作为PWM信号输入,并选择TI1FP1为触发信号(输入的信号被分为TI1FP1和TI1FP2),因此IC1捕获PWM信号周期,IC2捕获PWM信号占空比。而且由于PWM输入模式下,当捕获开始时,需要将CNT复位,所以我们需要配置定时器以从模式工作在复位模式下,最后使能捕获中断和高级定时器。

4、高级定时器中断服务函数配置

在函数TIM1_CC_IRQHandler中,如果是第一个上升沿中断,计数器CNT会被复位,锁存到CCR1寄存器的值是0,CCR2寄存器的值也是0,无法计算频率和占空比。当第二次上升沿到来的时候,CCR1和CCR2捕获到的才是有效的值。其中CCR1对应的是周期,CCR2对应的是占空比。我们配置的代码如下:

void TIM1_CC_IRQHandler(void)

{

    TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);


    IC1Value = TIM_GetCapture1(TIM1);

    IC2Value = TIM_GetCapture2(TIM1);


if (IC1Value != 0)

    {

      DutyCycle = (float)((IC2Value+1) * 100) / (IC1Value+1);


      Frequency = (72000000/((72-1)+1))/(float)(IC1Value+1);

  printf("占空比:%0.2f%%   频率:%0.2fHz\n", DutyCycle, Frequency);

     }

     else

     {

       DutyCycle = 0;

       Frequency = 0;

     }

}

5、主函数配置

main函数就是对上述函数的调用,配置示例如下: 

int main(void)

{   

CKS_USART_Init();

printf("CKS Timer Input Capture Demo start running...\r\n");


TIM1_GPIO_Configuration();

TIM1_NVIC_Configuration();

    TIM1_Input_Capture_Mode_Configuration();


while(1){}

}

至此,我们配置已完成。

6、下载验证

我们将编译好的程序下载至CKS32F107xx开发板,信号发生器和PWM输入引脚PA8通过杜邦线连接,USB转TTL模块分别与UART1—PA9和电脑相连,然后打开串口调试助手,查看打印信息。

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 19

相关阅读:MCU微课堂|CKS32F107xx TIM(一)

PWM模式简介

从上节课的定时器分类图中我们了解到,互补输出是高级定时器(TIM1、TIM8)所独有的,它可以输出两路互补信号。而在本节课中我们将通过一个简单的例程去介绍如何配置互补PWM输出,同时为了演示在电机控制领域的入门应用,该例程也增加了刹车和死区插入的功能。因此,接下来我们将先对PWM模式、互补输出、死区插入、刹车功能这四个概念作简要介绍,然后通过一个实验例程来演示PWM输出配置方法。

比较输出是通过比较计数器CNT和捕获/比较寄存器CCR的关系,来对输出电平进行置1、置0或翻转的操作,而PWM模式输出的是一种脉冲宽度可调的方波信号,同时也是定时器(高级和通用)比较输出模式中较为常用的一种,我们可以通过捕获/比较模式寄存器CCMRx的位OCxM[2:0]来配置输出。表1罗列了PWM1和PWM2的区别,并通过PWM1边沿对齐的波形示例图来简述下PWM输出的原理。

1.png

表1 PWM1和PWM2模式区别

2.png

图1 边沿对齐的PWM1波形(TIMx_ARR=8)

如上图所示,当CNT < CCR时,参考信号OCxREF输出为高,则通道OCx输出1;当CCR <= CNT <= ARR时,参考信号OCxREF输出为低,则通道OCx输出0。并且当计数器CNT与比较寄存器CCR值匹配时,比较中断寄存器CCxIF位会自动由硬件置 1;而CNT计数达到ARR的值时,会产生上溢事件,并且自动清零,然后重新向上计数,如此循环往复。因此,我们可以通过修改ARR和CCR的值来灵活调整PWM波形的频率和占空比。

定时器产生PWM 输出,除了上节课提到的ARR、PSC、CNT,不得不提的还有捕获/比较模式寄存器CCMR。该寄存器共有两个,CCMR1(控制输出通道1和2)和CCMR2(控制输出通道3和4),且上面一层对应比较输出,下面一层对应输入捕获,输入捕获模式将在下节课中介绍。由于本节课介绍的是互补PWM输出,所以模式设置位OCxM[2:0],必须设置为110/111,对应PWM1/PWM2,两者区别是极性相反。下图是CCMR1寄存器各位描述图,具体请参照CKS32F107xx参考手册。

3.png

图2 CCMR1寄存器各位描述图

互补输出和死区插入简介

接下来将先通过捕获/比较通道的输出概览图来展现输出控制过程。

4.png

图3 捕获/比较通道的输出部分(通道CH1~CH3)

如上图,红框中是CNT和CCR比较,输出参考信号OCxREF,衔接上文。绿框表明参考信号OCxREF在经过死区发生器之后会产生两路带死区的互补信号OCx_DT和OCxN_DT,并且需要特别说明下,高级定时器(TIM1、TIM8)只有通道CH1~CH3才有互补信号。蓝框是输出控制电路,若没有死区控制,则进入的信号即为OCxREF,且进入的信号会被分成极性相反的两路,并由寄存器CCER的位CCxP和CCxNP控制极性,由CxE和CCxNP位使能输出。若增加了刹车功能,则寄存器BDTR的位MOE、OSSI和OSSR将共同影响输出信号。

关于死区插入,其中一个较为典型的应用就是半桥驱动电路,比如上个时态MOS管M1导通,MOS管M2截止,下一时态需要M1截止,M2导通,由于工艺限制MOS管的关闭不能忽略不计,这就会导致M1和M2有同时导通的时间,进而损坏电路,所以可将M1关闭后等待一段时间再打开M2,这段时间可称为死区时间。死区时间是通过寄存器BDTR的位DTG[7:0]来配置,死区时间的大小需要根据输出通道相连接的器件特性来调整。下图为带死区插入的互补输出图,相信这样能大家有个更为直观的了解。

5.png

图4 带死区插入的互不输出

刹车功能简介

电路控制电机转动出现异常时,若用软件来关闭信号输出,出于软件延时的影响,可能带来不必要的后果。在此工况下,高级定时器中的刹车功能能发挥特定优势,用户只要将检测端连接到TIMx_BKIN引脚,当检测到非正常态,由硬件电路立刻关闭信号输出,电机即可停止转动。刹车功能是一种硬件保护保护,用户需要配置刹车和死区寄存器BDTR,该寄存器各位描述如下:

6.png

7.png

图5 BDTR寄存器各位描述图

互补PWM输出配置实验

本实验以高级定时器TIM1为例,并参照CKS32F107xx数据手册的引脚定义章节,分配了OC1(PA8)、OC1N(PB13)和BKIN(PB12)引脚。若我们在工程中选择BKIN引脚高电平有效,那么当BKIN引脚被置高时,两路互补PWM信号立刻停止输出。主要的编程要点如下。 

1、开启GPIO时钟,配置引脚

要使用GPIOA、GPIOB的相关引脚,需要开启端口时钟,调用的函数为:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

因为要将普通GPIO复用到定时器的相关引脚,所以要配置GPIO工作模式,示例代码如下,以PA8配置为例,PB13和PB12配置雷同:

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

2、TIM1时基结构体设置

鉴于前一章节已详述过,这里仅做代码演示,针对TIM1时基初始化示例代码格式如下: 

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Prescaler= 8;
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;  
TIM_TimeBaseStructure.TIM_Period=7;
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;  
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

根据前文定时时间的描述,当TIM1工作时钟配置为72MHz时,那么示例代码中配置的中断次时间为:(8+1)*(7+1) / 72000000 = 1us,则PWM输出的频率为1MHz。

3、TIM1输出比较结构体设置

通过配置TIM1_CCMR1的相关位来设置TIM1_CH1的PWM模式。在库函数中,PWM通道设置是通过函数TIM_OC1Init()~TIM_OC4Init()来设置的,不同通道的设置函数不一样,使用时需要注意区分,这里我们选用CH1通道和CH1N通道,选用的库函数如下:

void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

输出比较结构体在库函数中对应名称为TIM_OCInitTypeDef,下面是结构体的定义:

typedef struct
{
    uint16_t TIM_OCMode;
    uint16_t TIM_OutputState;uint16_t TIM_OutputNState;
    uint16_t TIM_Pulse;uint16_t TIM_OCPolarity;
    uint16_t TIM_OCNPolarity;uint16_t TIM_OCIdleState;
    uint16_t TIM_OCNIdleState;
} TIM_OCInitTypeDef;

参数TIM_OCMode是用来设置比较输出模式,这里我们选用PWM1模式。

参数TIM_OutputState和TIM_OutputNState用来配置OCx和OcxN通道输出使能。

参数TIM_Pulse是设置比较寄存器CCR的值,决定脉冲宽度,对应PWM1的占空比。

参数OCPolarity和TIM_OCNPolarity是设置OCx和OcxN通道极性,我们可以选择高电平或低电平有效。

参数TIM_OCNIdleState和TIM_OCNIdleState是设置空闲状态时OCx和OcxN通道输出的电平。

针对TIM1输出比较结构体初始化示例如下:

TIM_OCInitTypeDef      TIM_OCInitStructure;  
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_Pulse = 4;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);

在该示例中,设置了工作模式PWM1,使能了OCx和OcxN通道,配置了脉冲宽度、输出极性和空闲状态下的输出电平,所以PWM1的占空比为:CCR / (ARR + 1) = 50%。

4、TIM1断路和死区结构体设置

我们在库函数中设置刹车和死区参数是通过函数TIM_BDTRConfig来实现的,其结构体TIM_BDTRInitTypeDef中列出了各个成员,以下是结构体的定义:

Typedef struct  
{  
    uint16_t TIM_OSSRState;           
    uint16_t TIM_OSSIState;           
    uint16_t TIM_LOCKLevel;           
    uint16_t TIM_DeadTime;            
    uint16_t TIM_Break;             
    uint16_t TIM_BreakPolarity;       
    uint16_t TIM_AutomaticOutput;   
}TIM_BDTRInitTypeDef;

这里结构体中的成员可以对照上文BDTR寄存器的各位来理解,在此不再赘述,本实验中我们针对TIM1断路和死区结构体初始化示例如下:

TIM_BDTRInitTypeDef    TIM_BDTRInitStructure;
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
TIM_BDTRInitStructure.TIM_DeadTime = 11;
TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);

在该示例中,配置的死区时间为11 / 72000000 = 152ns,配置BKIN引脚为高电平有效。

5、使能TIM1和主输出

因为本实验是互补PWM输出,所以除了要开启定时器,也要对定时器主输出进行使能,配置示例如下: 

TIM_Cmd(TIM1, ENABLE);TIM_CtrlPWMOutputs(TIM1, ENABLE);

6、修改TIM1_CCR1来调整占空比

实际使用时,用户需要控制占空比来调整电机的转速,我们能通过修改TIM1_CCR1则可以控制CH1的输出占空比。在库函数中,修改TIM1_CCR1占空比的函数是:

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);

同样的,对于其他三个通道,也有如下对应函数供调用,用户使用时注意区分即可。

void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);

只要按照上述配置,用户在主函数中调用即可,我们示波器的两个输入通道分别接PA8、PB13引脚,可观测到两路互补带死区插入的PWM波形,频率为1MHz,占空比默认为50%(用户可自行调整),死区时间为152ns左右。

8.png

如果将BKIN引脚拉高,可观测到输出信号立刻停止,若松开则恢复默认输出。

9.png

至此,本实验已介绍完毕。

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 19

TIM简介

本节课我们将介绍互联型产品CKS32F107xx系列的定时器使用,CKS32F107xx的定时器资源比较丰富,包含2个高级定时器,10个通用定时器,2个基本定时器,以及两个看门狗定时器和一个系统定时器,共达17个定时器之多。关于定时器部分内容的讲解我们将分4个部分展开,本节将围绕定时器的定时原理展开,并以简单定时配置操作演示。

首先介绍下CKS32F107xx系列定时器的分类,详见下图:

1.png

图1 CKS32F107xx系列定时器的分类

1、计数器分辨率

指定时器的一个计数周期,例如:当TIM1的工作时钟配置为72MHz,则分辨率的范围为:1*(1000ns/ 72)~(2^16)*(1000ns/72)。

2、计数器类型(按计数的方向来划分)

向上计数:指的是从0开始到1,2...直到自己设置的计数上限值N,达到后再次从0开始计数,周而复始;

向下计数:指的是从设置的计数上限值N开始到N-1,N-2,...直到0,达到后再次从N开始计数,周而复始;

向上/下计数:指的是从0,1,2...N,然后再从N,N-1,N-2...0,周而复始。

3、预分频系数

可以通过设置该项系数来配置时基,如定时器工作在72MHz下,配置不分频则一个计数时基为13.9ns,配置成2分频则一个计数时基为27.8ns。

4、产生DMA

定时器(除TIM9~TIM14)的更新会发出DMA请求,这是因为在DMA通道中为定时器预留了一个通道。

5、捕获/比较通道:捕获就是定时器可以捕捉到通道的上升沿或者下降沿信号,比较就是定时器可以将计数器的值和装载值做比较,关于这部分将会在下后续章节展开。

6、互补输出:互补输出指的是输出的两个通道两个波形完全相反,通常运用在桥式电路中的互补PWM输出,这一部分将在后续章节展开。

定时原理

前面提到,定时器有三个分类,分别是高级、通用和基本,但核心都是时基,以下通过基本定时器的功能框图来阐述时基原理,后续三个章节将不再赘述。

2.png

图2 基本定时器框图

从上图可以看出,时基单元由计数器(CNT)、预分频器(PSC)和自动重装载寄存器(ARR)组成。定时器时钟TIMxCLK经过PSC预分频后,即为CK_CNT,而PSC是16位的预分频器,所以可对TIMxCLK进行1~65536分频。而自动重装载寄存器和计数器都为16位,当计数器向上计数到自动重装载寄存器的数值会产生更新事件,若使能了中断,则会产生溢出中断,同时计数器清零并从头开始计数。

关于定时器的定时时长取决于定时器进入一次中断的时长和进入中断的次数。若用变量Cnt_Period来记录中断次数,则定时时长为:(PSC+1)*(ARR+1)*Cnt_Period / TIMxCLK。

定制配置操作

搞清楚定时原理后,下面我们以基本定时器TIM6为例,进行相应的定时配置操作,实现500ms的定时,并以LED的翻转来进行演示,编程的要点如下所示。

1、TIM6时钟使能

TIM6时钟来自于APB1域,我们通过APB1总线下的时钟使能函数来使能TIM6的时钟。调用的函数为:   

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

2、TIM6时基结构体初始化

在标准库中,定时器时基的初始化是通过TIM_TimeBaseInit实现的:

TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);

参数结构体指针,结构体类型为TIM_TimeBaseInitTypeDef,下面是结构体的定义:

typedef struct

{

uint16_t TIM_Prescaler; // 预分频器

uint16_t TIM_CounterMode; // 计数模式

uint32_t TIM_Period; // 定时器周期

uint16_t TIM_ClockDivision; // 时钟分频

uint8_t TIM_RepetitionCounter; // 重复计算器

} TIM_TimeBaseInitTypeDef;

这个结构体一共有5个成员变量,需要强调的是,对于基本和通用定时器只有前面四个参数有用,最后参数TIM_RepetitionCounter仅针对高级定时器才有效的,后续章节会详解,在此不赘述。

参数一TIM_Prescaler是用来设置分频系数的,对应上表中的预分频系数。

参数二TIM_CounterMode是用来设置计数方式,如上表所述,可以设置为向上计数,向下计数方式还有向上\下计数(中央对齐计数)方式,比较常用的是向上计数TIM_CounterMode_Up和向下计数 TIM_CounterMode_Down。

参数三是设置自动重载计数周期值,可以通俗的理解成要定时的次数,这个是根据定时时间和时基做除法换算得到的,比如定时器现在计数1次,时间经过了500ms,要定时1s,那自动重载计数周期值为2。

参数四是用来设置时钟分频因子,这个参数与定时器的其他功能有密切,本节操作先按照TIM_CKD_DIV1来配置(不分频)。      

针对TIM6时基初始化示例代码格式如下:

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 4999;
TIM_TimeBaseStructure.TIM_Prescaler = 7199;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);

根据上文定时时间的描述,当TIM6工作时钟配置为72MHz时,那么示例代码中配置的中断次时间为:(7199+1)*(4999+1) / 72000000 = 500ms。

3、设置TIM6_DIER允许更新中断

为了实现到达指定时间后,方便用户能有一个后续操作,这需要我们开启TIM6的更新中断,在库函数里面定时器中断使能是通过TIM_ITConfig函数来实现的: 

void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

参数一是选择定时器号,取值为 TIM1~TIM17。参数二非常关键,是用来指明我们使能的定时器中断的类型,定时器中断的类型有很多种,包括更新中断TIM_IT_Update,触发中断TIM_IT_Trigger,以及输入捕获中断等等。参数三就很简单了,就是失能还是使能。例如我们要使能TIM6的更新中断,格式如下:

TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE );

4、TIM6中断优先级设置

在定时器中断使能之后,因为要产生中断,必不可少的要设置NVIC(向量中断控制器)来设置中断优先级。关于NVIC_Init函数实现中断优先级的设置可参照先前课程中的讲述,这里不再赘述。

5、使能TIM6

配置好定时器后,用户可以自行选择在合适的位置开启,且只有开启后,定时器才会进入工作状态,在标准库中使能定时器的函数是通过TIM_Cmd函数来实现的:

TIM_Cmd(TIM6, ENABLE);

6、编写终端服务函数

当中断产生后,程序会跳转至定时器TIM6的中断服务函数中,在这个函数中可通过状态寄存器的值来判断此次产生的中断属于什么类型,进而执行相关的操作,我们这里使用的是更新(溢出)中断,在处理完中断之后应该向TIM6_SR的最低位写0,来清除该中断标志。

在标准库中,通过读取中断状态寄存器的值来判断中断所属类型的函数是:ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t);因此若要判断定时器6是否发生更新(溢出)中断,可通过如下语句判断:

if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)

每当TIM6的更新(溢出)中断发生后,需要清除中断标志位,通过如下函数实现:   

TIM_ClearITPendingBit(TIM6, TIM_IT_Update);

所以,针对TIM6的中断服务函数,示例代码如下:

void TIM6_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
    {
        TIM_ClearITPendingBit(TIM6, TIM_IT_Update);//Clear TIM6 Update IT Flag
        GPIO_TogglePin(GPIOB, GPIO_Pin_12);//LED Toggle
    }
}

至此,本节课中简单定时操作例程已讲述完毕,程序编译后下载至开发板,用户可观测到LED每隔500ms翻转一次。

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 29

DAC简介

DAC(digital to analog converter)即数模转换器,它可以将数字信号转换为模拟信号。在常见的数字信号系统中,传感器信号被ADC模块把电压模拟信号转换成易于计算机存储、处理的数字信号,由计算机处理完成后,再由DAC模块转化输出电压模拟信号来驱动某些执行器件。

CKS32F107xx内部集成12位数字输入,电压输出型的DAC 模块,可以配置为8位或12位模式,也可以与DMA控制器配合使用,DAC工作在12位模式下时,数据可以设置为左对齐或右对齐。DAC模块有2个输出通道,每个通道都有单独的转换器;在双DAC模式下,2个通道可以独立地进行转换,也可以同时进行转换并同步地更新2个通道的输出;DAC可以通过引脚输入参考电压 VREF以获得更精确的转换结果。

CKS32F107xx DAC特点:

  • 两个DAC转换器:各有一个输出通道

  • 8位或12位单调输出

  • 12位模式下的左右数据对齐

  • 同步更新功能

  • 产生噪声波

  • 双DAC通道独立或同步转换

  • 每个通道都可使用DMA实现

  • 外部触发进行转换

  • 输入参考电压VREF+

CKS32F107xx DAC结构框图如下示:

1.png

图1 DAC通道模块框图

(1)VDDA和VSSA是DAC模块的供电引脚,VREF+是DAC模块的参考电压,DAC_OUTx是DAC的输出通道;当参考电压为VREF+时,DAC的输出电压是线性的(0~VREF+),12位模式下DAC输出电压计算公式如下:
  DAC_OUTx输出电压=VREF+*(DORx/4095)

(2)DAC输出是受DORx寄存器直接控制,但是不能直接往DORx寄存器写入数据,而是要通过DHRx间接传给DORx寄存器,实现对DAC输出的控制。如果未选择硬件触发,1个APB1时钟周期后,DHRx中存储的数据将自动转移到DORx寄存器;如果选择硬件触发,将在3个APB1时钟周期后进行转移;

(3)当DORx加载了DHRx内容时,模拟输出电压将在一端时间Tsetting后可用,具体取决于电源电压和模拟输出负载,可以从数据手册查到Tsetting的典型值为3us,最大值为4us,因此DAC的转换速度最快是250K左右。

DAC实验

DAC代码配置

(1)先使能GPIOA、DAC时钟;注意GPIOA对应APB2,DAC对应APB1;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );//使能PORTA通道时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟

(2)通道PA4配置,PA4必须配置为模拟输入,以减少寄生电流消耗;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;     // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;    // 模拟输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_4) ;//PA.4输出高

(3)外部触发选择,如下图2,如果使用外部触发,必须在TEN2=1(DAC通道2触发使能)时设置;本实验没使用到外部触发;

DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=0

2.png

图2 触发模式

(4)数据格式,可以设置单/双DAC通道、8bit/12bit、左/右对齐6种模式,如下图,本实验配置为:单ADC 12bit右对齐模式;

DAC_SetChannel1Data(DAC_Align_12b_R, 0);

3.png

图3 单DAC通道模式的数据寄存器

4.png

图4 双DAC通道模式的数据寄存器

(5)输出缓存,DAC集成了2个输出缓存,可以用来减少输出阻抗,无需外部运放即可直接驱动外部负载。每个DAC通道输出缓存可以通过设置DAC_CR寄存器的BOFFx位来使能或者关闭。注意:如果使能输出缓存,会出现输出不了0V,低电平最低只能到0.2V;本实验没有驱动实质性外部负载,故不需要配置缓存输出;

DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable; //DAC1输出缓存关闭

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 8

内部温度传感器

CKS32F107xx系列有一个内部的温度传感器,可以用来测量MCU及周围的温度(TA)。该温度传感器在内部和ADCx_IN16输入通道相连接,此通道把传感器输出的电压转换成数字值。温度传感器模拟输入推荐采样时间是17.1μs。 图1是温度传感器的方框图。

2.png

图1 温度传感器方框图

CKS32F107内部温度传感器的使用很简单,只要设置一下内部ADC,并激活其内部通道就差不多了。接下来我们介绍一下和温度传感器设置相关的2个地方。

第一个地方,我们要使用CKS32F107的内部温度传感器,必须先激活ADC的内部通道,这里通过ADC_CR2的AWDEN位(bit23)设置。设置该位为1则启用内部温度传感器。 

第二个地方,CKS32F107的内部温度传感器固定的连接在ADC 的通道16上,所以,我们在设置好ADC之后只要读取通道16的值,就是温度传感器返回来的电压值了。根据这个值,我们就可以计算出当前温度。计算公式如下:

3.png

上式中:V25为Vsense在25℃时的数值(典型值为:1.455)。Avg_Slope为温度与Vsense曲线的平均斜率(单位为mV/℃或 uv/℃)(典型值为3.3mV/℃)。 利用以上公式,我们就可以方便的计算出当前温度传感器的温度了。现在,我们就可以总结一下CKS3F107内部温度传感器使用的步骤了,如下:

1) 设置 ADC,开启内部温度传感器。

关于如何设置 ADC,在ADC采集的那一节已经介绍。不同的是上一节是读取外部通道的值,而内部温度传感器相当于把通道端口连接在内部温度传感器上。所以我们要开启内部温度传感器功能,使用如下函数:

ADC_TempSensorVrefintCmd(ENABLE);

2)读取通道16的AD值,计算结果。 在设置完之后,我们就可以读取温度传感器的电压值了,得到该值就可以用上面的公式计算温度值。

具体代码详见例程。

相关阅读:

CKS32F107xx系列 ADC(一)

CKS32F107xx系列 ADC(二)

MCU微课堂|CKS32F107xx ADC(三)

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 14

双重ADC模式

独立模式的ADC采集需要在一个通道采集并且转换完成后才会进行下一个通道的采集。而双重ADC的机制就是使用两个ADC同时采样一个或者多个通道。双重ADC模式较独立模式一个最大的优势就是提高了采样率,弥补了单个ADC 采样不够快的缺点。在有2个或以上ADC模块的产品中,可以使用双ADC模式。

在使用双ADC模式时,当转换配置成由外部事件触发时,用户必须将其设置成仅触发主ADC,从ADC设置成软件触发,这样可以防止意外的触发从转换。但是,主和从ADC的外部触发必须同时被激活。同时,在双ADC模式里,为了在主数据寄存器上读取从转换数据,

即使不使用DMA传输规则通道数据,也必须使能DMA位。

启用双ADC模式的时候,通过配置ADC_CR1寄存器的DUALMOD[3:0]位,可以有几种不同的模式,具体见如下表格:

1.png

这里我们选取同步规则模式来作为实验讲解。同步规则模式是ADC1和 ADC2 同时转换一个规则通道组,ADC1是主,ADC2是从,ADC1转换的结果放在ADC1_DR 的低16位,ADC2转换的结果放在ADC1_DR的高十六位。并且必须开启DMA功能。外部触发来自ADC1的规则组多路开关(由ADC1_CR2寄存器的EXTSEL[2:0]选择),它同时给ADC2提供同步触发。在同步规则模式中,必须转换具有相同时间长度的序列,或保证触发的间隔比2个序列中较长的序列长,否则当较长序列的转换还未完成时,具有较短序列的ADC转换可能会被重启。

为了简单起见,ADC1 我们选择软件触发,ADC2必须选择外部触发,这个外部触发来自于ADC1的规则组多路开关。例程中我们选取ADC1和ADC2各采集一个通道。

配置双重ADC规则同步模式代码如下:

/* ------------------DMA模式配置---------------- */
// 配置 DMA 初始化结构体
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&( ADCx_1->DR ));
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize =                                  
                  DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(ADC_DMA_CHANNEL, &DMA_InitStructure);

/* ----------------ADCx_1 模式配置--------------------- */
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;
ADC_InitStructure.ADC_ScanConvMode = ENABLE ;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;  
ADC_Init(ADCx_1, &ADC_InitStructure);
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
ADC_RegularChannelConfig(ADCx_1, ADCx_1_CHANNEL, 1,                         
                ADC_SampleTime_239Cycles5);ADC_DMACmd(ADCx_1, ENABLE);
/* ----------------ADCx_2 模式配置--------------------- */
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;
ADC_InitStructure.ADC_ScanConvMode = ENABLE ;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv =                           
                  ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = NOFCHANEL;  
ADC_Init(ADCx_2, &ADC_InitStructure);
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
ADC_RegularChannelConfig(ADCx_2, ADCx_2_CHANNEL, 1,ADC_SampleTime_239Cycles5);
ADC_ExternalTrigConvCmd(ADC2, ENABLE);

双重ADC规则模式和独立模式多通道的配置基本一样,只是有几点需要注意:

DMA缓冲区数据的大小为1,数组存放的数据类型为32位的;ADC工作模式要设置为同步规则模式;两个ADC的通道的采样时间需要一致;ADC1设置为软件触发;ADC2 设置为外部触发。

相关阅读:

CKS32F107xx系列 ADC(一)

CKS32F107xx系列 ADC(二)

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 11

相关阅读:CKS32F107xx系列 ADC(一)

采样DMA的原理和配置方法     

因为ADC规则组数据寄存器ADC_DR只有一个,如果使用多通道转换,那转换的数据就全部都挤在ADC_DR里面了,前一个时间点转换的通道数据,就会被下一个时间点的另外一个通道转换的数据覆盖掉,所以当通道转换完成后就应该把数据取走,或者开启DMA模式,把数据传输到内存里面,不然就会造成数据的覆盖。多通道ADC采集一般使用DMA进行数据传输,该方法更加高效方便。

注:只有ADC1拥有DMA功能。由ADC2转化的数据可以通过双ADC模式,利用ADC1的 DMA功能传输。 

DMA间接

直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。 两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。ADC1使用的是DMA1的通道1。

DMA初始化结构体详解

DMA_InitTypeDef初始化结构体结构体用于设置DMA的工作参数,其具体的定义如下:

typedef struct
{  
    uint32_t DMA_PeripheralBaseAddr;  
    uint32_t DMA_MemoryBaseAddr;      
    uint32_t DMA_DIR;                 
    uint32_t DMA_BufferSize;          
    uint32_t DMA_PeripheralInc;       
    uint32_t DMA_MemoryInc;           
    uint32_t DMA_PeripheralDataSize;  
    uint32_t DMA_MemoryDataSize;       
    uint32_t DMA_Mode;                 
    uint32_t DMA_Priority;           
    uint32_t DMA_M2M;              
}DMA_InitTypeDef;

1) DMA_PeripheralBaseAddr:外设地址,设定DMA_CPAR寄存器的值;一般设置为外设的数据寄存器地址,如果是存储器到存储器模式则设置为其中一个存储器地址。我么是把ADC采集到的数据通过DMA传输到存储器上,则外设地址为ADC的数据寄存器。

2) DMA_Memory0BaseAddr:存储器地址,设定DMA_CMAR寄存器值;一般设置为我们自定义的用来存放ADC数据的数组地址。

3) DMA_DIR:传输方向选择,可选外设到存储器、存储器到外设。它设定 DMA_CCR寄存器的DIR[1:0]位的值。这里并没有存储器到存储器的方向选择,当使用存储器到存储器时,只需要把其中一个存储器当作外设使用即可。本章选择为外设到存储器。 

4) DMA_BufferSize:设定待传输数据数目,初始化设定DMA_CNDTR寄存器的值,其大小等于我们定义的存储ADC数据的数组大小。

5)DMA_PeripheralInc:如果配置为 DMA_PeripheralInc_Enable,使能外设地址自动递增功能,它设定DMA_CCR寄存器的PINC位的值;因为ADC转换的数据都存放在一个数据寄存器中,则外设地址不变。 

6) DMA_MemoryInc:如果配置为 DMA_MemoryInc_Enable,使能存储器地址自动递增功能,它设定DMA_CCR寄存器的MINC位的值;因为我们自定义的数组用来存放两个数据,所以要使能存储器地址自动递增功能。

7) DMA_PeripheralDataSize:外设数据宽度,可选字节(8位)、半字(16位) 和字(32位),它设定DMA_CCR寄存器的PSIZE[1:0]位的值。

8) DMA_MemoryDataSize:存储器数据宽度,可选字节(8位)、半字(16位) 和字(32位),它设定DMA_CCR寄存器的MSIZE[1:0]位的值。外设和存储器单位均为两个字节。

9) DMA_Mode:DMA传输模式选择,可选一次传输或者循环传输,它设定 DMA_CCR寄存器的CIRC位的值。例程我们的ADC采集是持续循环进行的,所以使用循环传输模式。当启动了循环模式,数据传输的数目变为0时,将会自动地被恢复成配置通道时设置的初值,DMA操作将会继续进行。

ADC的工作参数配置

// 只使用一个ADC,属于单模式
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
// 扫描模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE ;
// 连续转换模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
// 不用外部触发转换,软件开启即可
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
// 转换结果右对齐
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
// 转换通道个数
ADC_InitStructure.ADC_NbrOfChannel = 2;
// 初始化ADC
ADC_Init(ADC1, &ADC_InitStructure);
// 配置ADC时钟N狿CLK2的8分频,即9MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
// 配置ADC 通道的转换顺序和采样时间
ADC_RegularChannelConfig(ADC1, ADC_CHANNEL1, 1, 
ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_CHANNEL2, 2, 
ADC_SampleTime_55Cycles5);
// 使能ADC DMA 请求
ADC_DMACmd(ADC1, ENABLE);
// 开启ADC ,并开始转换
ADC_Cmd(ADC1, ENABLE);
// 初始化ADC 校准寄存器  
ADC_ResetCalibration(ADC1);
// 等待校准寄存器初始化完成
while(ADC_GetResetCalibrationStatus(ADC1));
// ADC开始校准
ADC_StartCalibration(ADC1);
// 等待校准完成
while(ADC_GetCalibrationStatus(ADC1));
// 由于没有采用外部触发,所以使用软件触发ADC转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);

因为只是使用一个 ADC,所以模式配置为独立模式;例程中使用了ADC1的两个通道进行采集,因此需要开启扫描模式,当一个通道转换结束时,同一组的下一个通道将被自动转换;例程实现不间断的对外部模拟数据进行采集,因此使能连续转换模式。ADC的转换采用软件触发的方式,因此不使用外部触发转换信号。转换结果右对齐;转换通道数为2;因为是两个通道进行采集,所以调用ADC_RegularChannelConfig()函数设置每个通道的转换顺序和采样时间。

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 9

独立模式单通道采集

CKS32F107xx系列产品提供2个12位的模拟/数字转换器(ADC),每个ADC共用多达16个外部通道,各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。

A/D转换器的供电和参考电压

为了提高转换的精确度,ADC使用一个独立的电源供电,其电源引脚为VDDA和VSSA,从而过滤和屏蔽来自印刷电路板上的毛刺干扰。在进行硬件设计的时候,VDDA和VSSA必须分别连接到VDD和VSS。对于100脚封装的,为了确保输入为低压时获得更好精度,用户可以连接一个独立的外部参考电压ADC到VREF+和VREF-脚上,其中,VREF-引脚必须连接到VSSA,而VREF+的电压范围为2.4V~VDDA。对于64引脚封装的,没有VREF+和VREF-引脚,他们在芯片内部与ADC的电源(VDDA)和地(VSSA)相联。

ADC转换时间

ADC输入时钟ADC_CLK由PCLK2 经过分频产生,最大是14M,分频因子由RCC时钟配置寄存器RCC_CFGR的位15:14 ADCPRE[1:0]设置,可以是2/4/6/8分频,注意这里没有1分频。一般我们设置PCLK2=HCLK=72M。ADC使用若干个ADC_CLK周期对输入电压采样,采样周期数目可以通过ADC_SMPR1和 ADC_SMPR2寄存器中的SMP[2:0]位更改。每个通道可以分别用不同的时间采样。 总转换时间如下计算:TCONV=采样时间+12.5个周期。例如当ADCCLK=14MHz,采样时间为1.5周期,则总的转换时间TCONV=1.5+12.5=14周期=1us。

ADC数据寄存器

ADC转换完成后的数据输出寄存器。根据转换组的不同,规则组的完成转换的数据输出到ADC_DR寄存器,注入组的完成转换的数据输出到ADC_JDRx寄存器。假如是使用双重模式,规则组的数据也是存放在ADC_DR寄存器。ADC规则组数据寄存器ADC_DR是一个32位的寄存器,独立模式时只使用到该寄存器低16位保存ADC1/2的规则转换数据。在双ADC模式下,高16位用于保存ADC2转换的数据,低16位用于保存ADC1转换的数据。因为ADC的精度是12位的,ADC_DR寄存器无论高16位还是低16位,存放数据的位宽都是16 位的,所以允许选择数据对齐方式。由ADC_CR2寄存器的ALIGN位设置数据对齐方式,可选择:右对齐或者左对齐。如果使用多通道转换,那么这些通道的数据 也会存放在ADC_DR里面,按照规则组的顺序,上一个通道转换的数据,会被下一个通道转换的数据覆盖掉,所以当通道转换完成后要及时把数据取走。比较常用的方法是使用DMA模式。当规则组的通道转换结束时,就会产生DMA请求,这样就可以及时把转换的数据搬运到用户指定的目的地址存放。注意:只有ADC1可以产生DAM请求,而由ADC2转换的数据可以通过双ADC模式,利用ADC1的 DMA功能传输。

ADC中断

ADC中断可分为三种:规则组转换结束中断、注入组转换结束中断、设置了模拟看门狗状态位中断。它们都有独立的中断使能位,分别由ADC_CR 寄存器的EOCIE、JEOCIE、AWDIE位设置,对应的标志位分别是EOC、JEOC、AWD。

ADC初始化结构体详解

ADC_InitTypeDef结构体用于设置ADC的工作参数,并由标准库函数ADC_Init()调用这些设定参数进入设置外设相应的寄存器,达到配置外设工作环境的目的。其具体的定义如下:

typedef struct
{  
    uint32_t ADC_Mode;                       
    FunctionalState ADC_ScanConvMode;       
    FunctionalState ADC_ContinuousConvMode;  
    uint32_t ADC_ExternalTrigConv;            
    uint32_t ADC_DataAlign;                  
    uint8_t ADC_NbrOfChannel;              
}ADC_InitTypeDef;

ADC_Mode:配置ADC的模式,当使用一个ADC时是独立模式,使用两个ADC 时是双模式,在双模式下还有很多细分模式可选,具体配置ADC_CR1:DUALMOD位。

ScanConvMode:可选参数为ENABLE和DISABLE,配置是否使用扫描。如果是单通道AD转换使用DISABLE,如果是多通道AD转换使用ENABLE,具体配置 ADC_CR1:SCAN位。

ADC_ContinuousConvMode:可选参数为 ENABLE 和 DISABLE,配置是启动自动连续转换还是单次转换。使用ENABLE配置为使能自动连续转换;使用 DISABLE 配置为单次转换,转换一次后停止需要手动控制才重新启动转换,具体配置 ADC_CR2:CON位。

ADC_ExternalTrigConv:外部触发选择,ADC有很多外部触发条件,可根据项目需求配置触发来源。实际上,我们一般使用软件自动触发。

ADC_DataAlign:转换结果数据对齐模式,可选右对齐ADC_DataAlign_Right 或者左对齐ADC_DataAlign_Left。

ADC_NbrOfChannel:AD转换通道数目,根据实际设置即可。具体的通道数和通道的转换顺序是配置规则序列或注入序列寄存器。

CKS32F103XX ADC单通道采集实验

本实验使用规则组单通道的单次转换模式,并且通过软件触发,即由ADC_CR2寄存器的SWSTART位启动。下面讲解其详细设置步骤:

1) 开启PA口时钟和ADC1时钟,设置PA1为模拟输入。

CKS32F107xx的ADC通道1在PA1上,所以,我们先要使能PORTA的时钟和ADC1时钟,然后设置PA1为模拟输入。使能GPIOA和ADC时钟用RCC_APB2PeriphClockCmd函数,设置 PA1的输入方式,使用GPIO_Init函数即可。

2) 复位 ADC1,同时设置 ADC1 分频因子。 

开启ADC1时钟之后,我们要复位ADC1,将ADC1的全部寄存器重设为缺省值之后我们 就可以通过RCC_CFGR设置ADC1的分频因子。分频因子要确保ADC1的时钟(ADCCLK)不要超过14Mhz。这个我们设置分频因子位6,时钟为72/6=12MHz,库函数的实现方法是: 

RCC_ADCCLKConfig(RCC_PCLK2_Div6);

ADC 时钟复位的方法是:

ADC_DeInit(ADC1);

3) 初始化ADC1参数,设置ADC1的工作模式以及规则序列的相关信息。 

在设置完分频因子之后,我们就可以开始 ADC1 的模式配置了,设置单次转换模式、触发方式选择、数据对齐方式等都在这一步实现。同时,我们还要设置ADC1规则序列的相关信息,我们这里只有一个通道,并且是单次转换的,所以设置规则序列中通道数为1。这些在库函数中是通过函数ADC_Init实现的。

4) 使能ADC并校准。 

在设置完了以上信息后,我们就使能AD转换器,执行复位校准和AD校准,注意这两步 是必须的!不校准将导致结果很不准确。 使能指定ADC的方法是:

ADC_Cmd(ADC1, ENABLE);

执行复位校准的方法是:

ADC_ResetCalibration(ADC1);

执行ADC校准的方法是:

ADC_StartCalibration(ADC1);

每次进行校准之后要等待校准结束。这里是通过获取校准状态来判断是否校准是否结束。等待复位校准结束函数为:

while(ADC_GetResetCalibrationStatus(ADC1));

等待AD校准结束函数为:

while(ADC_GetCalibrationStatus(ADC1));

5)读取ADC值。 

在上面的校准完成之后,ADC就算准备好了。接下来我们要做的就是设置规则序列1里面的通道,采样顺序,以及通道的采样周期,然后启动ADC转换。在转换结束后,读取ADC 转 换结果值就是了。我们这里是规则序列中的第1个转换,同时采样周期为239.5,所以设置为:

ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );

软件开启ADC转换的方法是:

ADC_SoftwareStartConvCmd(ADC1, ENABLE);

开启转换之后,就可以获取转换ADC转换结果数据,方法是:

ADC_GetConversionValue(ADC1);

同时在AD转换中,我们还要根据状态寄存器的标志位来获取AD转换的各个状态信息。库函数获取AD转换的状态信息的函数是:

FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);

比如我们要判断ADC1d的转换是否结束,方法是: 

while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));

通过以上几个步骤的设置,我们就能正常的使用CKS32F107xx的ADC1来执行AD转换操作了。

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 20

本章节首先将以MCU开发人员常接触到的“中断”、“事件”和“中断事件”三个名词的概念展开,然后去阐述彼此的主要区别,最后借助MCU的GPIO外部中断/事件控制器(EXTI)的传输路径来加深对上述概念的理解。

概念简介

中断    

为了具化下述概念,特引用MCU运行过程中外设模块的触发和反馈来解释说明。

MCU执行程序时,由于发生了某种特定的事件(外部或内部),引起MCU暂时中断正在运行的程序,转去执行与该事件相关的中断服务程序,该事件处理完后又返回被中断的程序继续执行,这一过程称之为中断或中断响应。

事件

事件是指CKS32F107xx系统中发生的一些特定的状态变化,譬如:外部输入电平变化、定时器溢出、FIFO非空、串口接收/发送数据、AD转换完成、外设使能、初始化等。而事件与中断事件是包含关系,即事件可分为中断事件或非中断事件。比如AD转换并不会导致中断发生,因而归类为非中断事件,但AD转换结束就是一个中断事件。

中断事件

中断事件,顾名思义是指能导致中断发生的事件。值得注意的是中断事件最终是否导致后续中断的正常触发,还需要确保是否开启了该中断事件的中断使能。

中断与事件的主要区别

(1)中断与中断事件属于前后关联的因果关系,二者在时序和行为上不一样,即中断事件是中断的触发源;

(2)事件与中断事件为包含关系;

(3)中断有可能被更高优先级的中断屏蔽,但事件不会;

(4)中断一定要有中断服务函数,但是事件没有;

(5)中断一定要MCU的介入,但事件执行操作,可以不需要MCU干预;

(6)中断是软件级的操作,而事件是硬件级。

外部中断/事件控制器(EXTI)

对于互联型产品CKS32F107xx系列的EXTI,它支持20个软件的事件/中断请求,且每个中断/事件都有独立的触发和屏蔽,每个中断线都有专用的状态位。下图为GPIO的EXTI的框图,同时也是外部中断线或外部事件线的示意图。

1.png

图1 中断/事件线示意图

从上文可知晓,一个能够触发中断的事件在触发配置时就出现两种可能,即允许产生中断或禁止产生中断,这随之引出事件模式和中断模式两个概念。从上图不难看出,每根信号线上划有一条斜线,并标注数字20,表明这样的线路共有20条,图中的蓝色虚线箭头,标出了中断模式下的传输路径,而图中的绿色箭头则标识了事件模式下的传输路径,下文一一说明。

1)中断模式下的传输路径(①-②-③-④-⑤)

①输入线:EXTI控制器具有20个中断/事件输入线,这些输入线可以连接到任意一个 GPIO或一些外设的事件,当这些输入线上的信号发生电平变化时,EXTI控制器会检测到并触发中断或事件。

②边沿检测电路:EXTI控制器允许用户选择触发方式,可通过设置上升沿、下降沿触发选择寄存器中相应的位来控制信号的触发。

③或门电路:一端输入信号线由边沿检测电路提供,另一端由软件中断事件寄存器(可以使用软件来启动中断/事件线)提供,此处软件可以优先于外部信号请求一个中断或事件,即当软件中断事件寄存器的对应位为”1”时,不管外部信号如何,则输出有效信号1,并且输出的信号会被保存到请求挂起寄存器内,当电路输出为1就会把请求挂起寄存器对应位置1。

④与门电路:一端由或门电路的输出提供,另一端由中断屏蔽寄存器提供,只有当两者都为有效信号1,才会输出有效信号1,即向NVIC中断控制器发出一个中断请求。

⑤将请求挂起寄存器的内容输入到NVIC中,从而实现对中断事件的控制。

2)事件模式下的传输路径(①-②-③-⑥-⑦-⑧)

⑥与门电路:此处与门电路与标号④与门电路类似,用于引入事件屏蔽寄存器的控制,只有两个均输入有效电平1时,才会输出有效信号1。

⑦脉冲发生器:当标号⑥与门电路输出有效信号1时,脉冲发生器会输出一个脉冲信号。

⑧脉冲信号:由脉冲发生器产生,通常用于触发定时器、ADC等。

综上所述,从外部激励源来看,无论中断模式还是事件模式的传输路径在标识①~③是一致的,主要区别是中断需要MCU介入,且同时需要有中断处理函数的参与才会形成中断后的结果,但事件是通过脉冲发生器发出的脉冲信号,进而由硬件自行完成该事件并产生响应的结果。所以,从节省MCU开销,提高系统运行效率来看,事件模式不失为一种提高MCU处理能力的快速响应机制。

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 21

页面

订阅 RSS - CKS32F107xx