MM32F5270

引入

MM32F5270的ADC可配置4个注入通道来扩展转换通道,那么可进行转换的通道最多可达20个。

特点

1. 通道优先级

注入通道转换顺序从JSQ0 ~ JSQ3优先级由高到底依次转换。

2. 通道转换方式

支持单端转换、差分转换和伪差分转换。

3. 数据补偿

注入通道转换的值减去对应注入补偿寄存器的值,补偿后的结果可能为负值,因此配置了注入补偿寄存器的注入通道转换结果是有符号数据,根据数据对齐方式不同符号位SEXT不同。图3.1描述了数据分辨率与数据补偿左对齐关系,图3.2描述数据对齐及注入通道符号位位置。

1.png

图3.1 数据分辨率与数据补偿左对齐

2.png

图3.2 数据对齐

4. 注入通道中断

注入通道序列转换结束中断(JEOSIF)、注入通道转换结束中断(JEOCIF)和注入通道采样结束中断(JEOSMPIF)。

5. 注入通道启动转换

主要有两种方式,第一种是在任意通道工作模式下,使能自动注入ANYCR_JAUTO,当任意通道序列转换完成后,注入通道会自动紧跟着转换;第二种是自行设置注入转换使能ANYCR_JADST,即可转换注入通道。注意注入通道转换完成后ANYCR_JADST,将被硬自动清除。

例程介绍

在任意通道工作模式下,配置规则组转换序列,再配置注入组转换序列,使能自动注入,查看注入通道是否转换完成。

具体配置步骤:

1、配置ADC初始化结构体,配置分辨率12位、单周期转换、数据右对齐、单端转换、参考内部电压

2、配置规则组转换序列和每个规则通道的采样时间,注入组转换序列和每个注入通道的采样时间

3、使能注入通道自动转换

4、使能ADC

参数定义如下:

#define APP_ADC_SEQ_LEN 3u
#define APP_ADC_EXT_SEQ_LEN 2u
/* channels in any sequence. */
uint32_t app_adc_seq_channels[APP_ADC_SEQ_LEN] =
{
    BOARD_ADC_CHN_NUM_1,
    BOARD_ADC_CHN_NUM_4,
    BOARD_ADC_CHN_NUM_5,
};
/* channels in inject sequence. */
uint32_t app_adc_extseq_channels[APP_ADC_EXT_SEQ_LEN] =
{
    BOARD_ADC_CHN_NUM_5,
    BOARD_ADC_CHN_NUM_1
};

配置代码如下:

/* setup the converter. */
ADC_Init_Type adc_init;
adc_init.Resolution = ADC_Resolution_Alt0;
adc_init.ConvMode = ADC_ConvMode_SeqOneTime; 
adc_init.Align = ADC_Align_Right;
adc_init.SingleDiffMode = ADC_SingleDiffConvMode_SingleEnd; 
adc_init.SingleVolt = ADC_SingleConvVref_Internal;  
ADC_Init(ADC1, &adc_init);

/* setup the conversion any sequence. */
/* config regular sequence channel */
for (uint32_t i = 0u; i < APP_ADC_SEQ_LEN; i++)
{
    ADC_EnableSeqSlot(ADC1,i,app_adc_seq_channels[i]);         
    ADC_SetChnSampleTime(ADC1,app_adc_seq_channels[i], ADC_SampleTime_Alt7);
}
/* config inject channel. */
for (uint32_t i = 0u; i < APP_ADC_EXT_SEQ_LEN; i++)
{
    ADC_EnableExtSeqSlot(ADC1, i, app_adc_extseq_channels[i]);
    ADC_SetChnSampleTime(ADC1,app_adc_extseq_channels[i], ADC_SampleTime_Alt7);
}
/* enable the inject atuo conversion. */
ADC_EnableAutoExtSeqSlot(ADC1, true);
/* power on the converter. */
ADC_Enable(ADC1, true);

注:ADC转换前必须获取校验因子,否则无法转换。

结果如下图:

3.gif

来源:灵动MM32MCU

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

围观 7

脉冲信号用于设备控制是比较常见的,但在一些情况下,我们希望精准的控制脉冲的数量以实现对运动的精确控制,实现的方式也有多种多样。定时器是单片机内部最基础且常用的外设,有着非常丰富的功能,如输入功能(测量输入信号的脉冲宽度、频率,PWM 输入等),输出功能(PWM 输出、死区时间可编程的互补输出、 单脉冲模式输出等) ,容易想到使用定时器输出PWM来实现此类操作。

MM32F5270系列集成有丰富的外设模块,其中定时器部分包括 2 个 16 位高级定时器, 2 个 16 位通用定时器、 2 个 32 位通用定时器, 2 个 16 位基础定时器和1 个低功耗定时器。以TIM1为例,该模块主要由输入单元、输出单元、时基单元、捕获/比较模块、刹车单元等结构组成,功能框图如下:

1.png

这里以MM32F5270定时器应用为例,介绍几种常用的精准输出脉冲数量的方法:

1►中断计数方式

定时器配置为PWM输出模式,在PWM中断程序中计数,判断PWM输出次数达到设定值时,停止PWM输出。

中断计数的方式实现起来简单,但也存在明显的缺点。当PWM频率较高时,频繁的中断将影响程序运行的效率,占用大量的MCU资源,这在大多数情况下是不可接受的。以下几种方式较为优化。

2►定时器单脉冲重复计数

定时器单脉冲输出是定时器比较输出中的一种模式,在定时器比较输出模式的基础上进行配置。单脉冲模式(OPM)下,计数器响应一个激励,产生一个脉宽可调的脉冲。配置 TIMx_CR1 寄存器的OPM=1,选择单脉冲模式。

2.png

单脉冲模式可以使定时器输出1个脉冲,而重复计数器可以用来调整更新事件产生的频率。

边沿对齐模式下,向上计数时,重复计数器在计数器每次上溢时递减;向下计数时,重复计数器在计数器每次下溢时递减。中央对齐模式下,重复计数器在计数器上溢和下溢时皆递减。通过配置 TIMx_RCR 寄存器的 REP 来调整更新事件产生的频率,重复计数器在 REP+1 个计数周期后产生更新事件。

3.png

配置TIM1输出PWM,使能单脉冲模式,配置REP(重复计数器的值)为9,即TIM1在输出10个脉冲后发生更新事件,相关代码如下:

    void TIM1_Monopulse_Init(u16 arr, u16 psc)  
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStruct;
    TIM_OCInitTypeDef  TIM_OCInitStruct;
    TIM_ICInitTypeDef  TIM_ICInitStruct;
    RCC_APB2PeriphClockCmd(RCC_APB2ENR_TIM1, ENABLE);
    TIM_DeInit(TIM1);
    TIM_TimeBaseStructInit(&TIM_TimeBaseStruct);
    TIM_TimeBaseStruct.TIM_Period = arr;
    TIM_TimeBaseStruct.TIM_Prescaler = psc;  
    TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;  
    TIM_TimeBaseStruct.TIM_RepetitionCounter = 9; 
    TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; 
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStruct);

    TIM_OCStructInit(&TIM_OCInitStruct);  
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;  
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;  
    TIM_OCInitStruct.TIM_Pulse = arr / 2; 
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;  
    TIM_OCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Reset;
    TIM_OC1Init(TIM1, &TIM_OCInitStruct); 
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); 
    TIM_ARRPreloadConfig(TIM1, ENABLE);    
    TIM_SelectOnePulseMode(TIM1, TIM_OPMode_Single);  
    TIM_SetCounter(TIM1, 0);
    TIM_CtrlPWMOutputs(TIM1, ENABLE);  

    TIM_Cmd(TIM1, ENABLE); 
}

逻辑分析仪接PA8(程序中配置PA8作为TIM1_CH1),观测输出波形如下:

4.png

由于REP只有8位,所以它最大是255,当然也可以进行一些判断后再次赋值,目前只有高级定时器具有重复计数功能。

3►DMA方式

使用DMA功能更新PWM的输出,DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。它允许不同速度的硬件装置来沟通,而不需要依赖于MPU的大量中断负载。该方式占用很少的MCU资源,实现脉冲发送的精确控制。

通过设置DMA传输数据的数量,可以控制发送的脉冲数。通过设置不同的装载值和顺序,可以使用不同频率和脉宽。

TIMx_DCR 和 TIMx_DMAR 寄存器跟 DMA 模式相关。DMA 控制器的目标是唯一的,必须指向TIMx_DMAR 寄存器。开启 DMA 使能后,在给定的 TIMx 事件发生时, TIMx 会给 DMA 发送请求。对TIMx_DMAR 寄存器的每次写操作都被重定向到一个 TIMx 寄存器。

TIMx_DMAR  连续模式 DMA 地址寄存器:

5.png

TIMx_DCR  DMA 控制寄存器:

6.png

程序中配置TIM1的更新周期为10ms。

    TIM1_PWM_Init(10000 - 1, SystemCoreClock / 1000000 - 1);

定义一个数组,元素的数量表示可以控制发送的脉冲数,元素的值表示脉宽。

    static u16 data[10] = {1000,2000,3000,4000,5000,6000,7000,8000,9000,0};

配置TIM1输出PWM,相关代码同上,使能COM的DMA请求,配置DMA初始化,使能DMA传输完成中断,TIM1_CH1对应DMA1_Channel2。

    void TIM1_DMA_Init(void)
{
    DMA_InitTypeDef  DMA_InitStruct;
    DMA_Channel_TypeDef* channel;
    channel = DMA1_Channel2;
    RCC_DMA_ClockCmd(DMA1, ENABLE);

    DMA_DeInit(channel);
    DMA_StructInit(&DMA_InitStruct);
    //Transfer register address
    DMA_InitStruct.DMA_PeripheralBaseAddr    = (u32) & (TIM1 ->CCR1);
    //Transfer memory address
    DMA_InitStruct.DMA_MemoryBaseAddr        = (u32)data;
    //Transfer direction, from memory to register
    DMA_InitStruct.DMA_DIR                   = DMA_DIR_PeripheralDST;
    DMA_InitStruct.DMA_BufferSize            = 10;
    DMA_InitStruct.DMA_PeripheralInc         = DMA_PeripheralInc_Disable;
    //Transfer completed memory address increment
    DMA_InitStruct.DMA_MemoryInc             = DMA_MemoryInc_Enable;
    DMA_InitStruct.DMA_PeripheralDataSize    = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStruct.DMA_MemoryDataSize        = DMA_MemoryDataSize_HalfWord;
    DMA_InitStruct.DMA_Mode                  = DMA_Mode_Normal;//DMA_Mode_Circular;
    DMA_InitStruct.DMA_Priority              = DMA_Priority_High;
    DMA_InitStruct.DMA_M2M                   = DMA_M2M_Disable;
    DMA_InitStruct.DMA_Auto_reload = DMA_Auto_Reload_Disable;
    DMA_Init(channel, &DMA_InitStruct);

    DMA_ITConfig(channel, DMA_IT_TC, ENABLE);
    DMA_Cmd(DMA1_Channel2, ENABLE);
}

DMA中断服务子程序:

    void DMA1_Channel2_IRQHandler(void)
{
    if (DMA_GetITStatus(DMA1_IT_TC2)) {
        DMA_ClearITPendingBit(DMA1_IT_TC2);
        TIM_Cmd(TIM1, DISABLE);
    }
}

逻辑分析仪接PA8(程序中配置PA8作为TIM1_CH1),观测输出波形如下:

7.png

输出9个脉冲,脉宽分别为10%、20%、30%......90%。

DMA方式算是一个很确定的方式,不会丢失脉冲。当需要发送较多数量的脉冲时,则可以使用DMA传输完成中断中切换DMA传输的数据起始地址及发送数量。

4►主从模式

定时器同步功能可以配置多个定时器在内部相连。

利用定时器的主从模式,即一个是主定时器,一个是从定时器,由主定时器输出脉冲信号,主定时器产生的更新触发传递给从定时器进行计数,溢出时触发从定时器的中断服务函数。通过主从定时器进行设定,不占用主程序时钟,且能精准控制。

主从关系要遵循参考手册中所提供的配置,TIMx之间的互联:

8.png

参考TIMx_CR2和TIMx_SMCR寄存器配置主从模式。

TIMx_CR2 控制寄存器 2:

9.png

TIMx_SMCR 从模式控制寄存器:

10.png11.png

配置TIM1为主模式,输出PWM:

    void TIM1_Master_Init(u16 arr, u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStruct;
    TIM_OCInitTypeDef  TIM_OCInitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_TIM1, ENABLE);

    TIM_TimeBaseStructInit(&TIM_TimeBaseStruct);
    TIM_TimeBaseStruct.TIM_Period = arr;
    TIM_TimeBaseStruct.TIM_Prescaler = psc;
    TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStruct);

    TIM_OCStructInit(&TIM_OCInitStruct);
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStruct.TIM_Pulse = 499;
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM1, &TIM_OCInitStruct);

    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
    TIM_ARRPreloadConfig(TIM1, ENABLE);
    TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);   
    TIM_SelectOutputTrigger(TIM1, TIM_TRIGSource_Update);

    TIM_SetCounter(TIM1, 0);
    TIM_CtrlPWMOutputs(TIM1, ENABLE);
    TIM_Cmd(TIM1, ENABLE);
}

配置TIM3为从模式,选择ITR0触发(对应内部触发源TIM1),使能更新中断:

    void TIM3_Slave_Init(u16 arr, u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStruct;
    RCC_APB1PeriphClockCmd(RCC_APB1ENR_TIM3, ENABLE);

    TIM_TimeBaseStructInit(&TIM_TimeBaseStruct);
    TIM_TimeBaseStruct.TIM_Period = arr;
    TIM_TimeBaseStruct.TIM_Prescaler = psc;
    TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStruct);

    TIM_ARRPreloadConfig(TIM3, DISABLE);

    TIM_SelectInputTrigger(TIM3, TIM_TS_ITR0);  
    TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_External1);
    TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);

    TIM_ClearFlag(TIM3, TIM_FLAG_Update);
    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

    TIM_SetCounter(TIM3, 0);
    TIM_Cmd(TIM3, ENABLE);
}

TIM3控制脉冲数量,此处设置为10:

    TIM3_Slave_Init(10, 0);

TIM3中断服务子程序:

    void TIM3_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);    
        TIM_CtrlPWMOutputs(TIM1, DISABLE);
        TIM_Cmd(TIM1, DISABLE);
        TIM_Cmd(TIM3, DISABLE);
        TIM_ITConfig(TIM3, TIM_IT_Update, DISABLE);
    }
}

逻辑分析仪接PA8(程序中配置PA8作为TIM1_CH1),观测输出波形如下:

12.png

TIM1输出10个脉冲后停止。

以上简要列举了几种控制脉冲数量输出的方式,以MM32F5270为例演示其实现的可行性。在实际应用中,几种方法各有优缺点,具体的方式还需要根据资源和需求进行综合考虑。

来源:灵动MM32MCU

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

围观 16

1、单脉冲输出

定时器单脉冲输出是比较输出应用中的一种模式,所谓的单脉冲就是通过配置定时器使其在一个可控延时后,产生一个脉宽可控的脉冲。

单脉冲模式(One Pulse Mode)下,计数器响应一个激励,产生一个脉宽可调的脉冲。配置 TIMx_CR1 寄存器的OPM=1,选择单脉冲模式,触发信号有效沿或配置 CEN=1 都可以启动计数器,直到下个更新事件发生或配置 CEN=0 时,计数器停止计数。

产生脉冲的必要条件是比较值与计数器的初始值不同,所以在计数器启动之前的必要配置如下:

  • 递增计数方式:计数器 CNT < CCRx ≤ ARR。

  • 递减计数方式:计数器 CNT > CCRx。

1.png

图 1 单脉冲模式

例如,在 TI2 检测到上升沿,延迟 tDELAY 之后,在 OC2 上产生一个长度为 tPULSE 的正脉冲。配置 TI2FP2 作为触发源:

1)配置 TIMx_CCMR1 寄存器中的 CC2S = 01,将 TI2FP2 映射到 TI2。

2)配置 TIMx_CCER 寄存器中的 CC2P = 0,检测 TI2FP2 的上升沿。

3)配置 TIMx_SMCR 寄存器中的 TS = 110, TI2FP2 作为从模式控制器的触发(TRGI)。

4)配置 TIMx_SMCR 寄存器中的 SMS = 110,选择触发模式, TI2FP2 使能计数器工作。

OPM 的波形由 TIMx_ARR 和 TIMx_CCR1 决定(要考虑时钟频率和计数器预分频器):由 TIMx_CCR1寄存器的值和 CNT 初始值决定触发信号与单脉冲开始之间的延迟 tDELAY, TIMx_ARR - TIMx_CCR1 的值为脉冲的宽度 tPULSE。

下面是一个产生负脉冲的例子,即发生比较匹配时产生从 1 到 0 的波形,计数器达到预装载值时产生一个从 0 到 1 的波形:

1)配置 TIMx_CCMR1 寄存器 OC1M = 111,选择 PWM 模式 2。

2)配置 TIMx_CCER 寄存器 CC1P = 1,输出低电平有效。

3)配置 TIMx_CCMR1 中 OC1PE = 1 和 TIMx_CR1 寄存器中 ARPE=1,使能预装载寄存器。

4)配置 TIMx_CCR1 寄存器和 TIMx_ARR 寄存器。

5)配置 TIMx_EGR 寄存器 UG=1 产生一个更新事件。

6)等待在 TI2 上的一个外部触发事件。

此例中, TIMx_CR1 寄存器中的 DIR=0、 CMS=0、 OPM= 1,在下一个更新事件(当计数器从自动装载值返回到 0)时停止计数。

2、实验

2.1  实验说明

配置TIM1_CH1单脉冲输出,TIM1_CH2输入捕获,TIM1从模式选择触发模式,TIM3_CH1输出PWM,触发TIM1计数器开始计数。当TIM1_CH2捕捉到有效信号时,TIM1计数器开始计数,按照配置好的脉宽,TIM1_CH1输出一个脉冲信号。

2.2  程序编写

程序部分参考MM32提供的例程,相关代码在此基础上更改。

2.21 GPIO初始化

配置PA8作为TIM1_CH1、PA9作为TIM1_CH2

    void TIM1_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    RCC_GPIO_ClockCmd(GPIOA, ENABLE);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1);

    GPIO_InitStruct.GPIO_Pin     = GPIO_Pin_8;
    GPIO_InitStruct.GPIO_Mode    = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed   = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin     = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode    = GPIO_Mode_IPD;
    GPIO_InitStruct.GPIO_Speed   = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
}

同样的,配置PB4作为TIM3_CH1,代码略。

2.22 TIM1初始化

程序中调用TIM1_Monopulse_Init()函数配置TIM1的自动预装载值为(10000-1),预分频器的值为(SystemCoreClock / 1000000 - 1),即TIM1定时器发生更新事件的周期为10ms。

    TIM1_Monopulse_Init(10000 - 1, SystemCoreClock / 1000000 - 1);

在该函数中配置了TIM1_CH1输出PWM,输出信号极性为高电平有效,脉冲宽度为50%,使能TIM1单脉冲输出模式。配置TIM1_CH2输入捕获,从模式触发源选择TI2FP2作为计数器的触发输入,在信号的上升沿启动。详见函数定义如下:

    void TIM1_Monopulse_Init(u16 arr, u16 psc)  
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStruct;
    TIM_OCInitTypeDef  TIM_OCInitStruct;
    TIM_ICInitTypeDef  TIM_ICInitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_TIM1, ENABLE);
    TIM_DeInit(TIM1);
    TIM_TimeBaseStructInit(&TIM_TimeBaseStruct);
    TIM_TimeBaseStruct.TIM_Period = arr;
    TIM_TimeBaseStruct.TIM_Prescaler = psc;
    TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStruct.TIM_RepetitionCounter = 0; 
    TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStruct);

    TIM_OCStructInit(&TIM_OCInitStruct); 
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; 
    TIM_OCInitStruct.TIM_Pulse = arr / 2;
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; 
    TIM_OCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Reset;
    TIM_OC1Init(TIM1, &TIM_OCInitStruct);  
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
    TIM_ICStructInit(&TIM_ICInitStruct);

    TIM_ICInitStruct.TIM_Channel     = TIM_Channel_2; 
    TIM_ICInitStruct.TIM_ICPolarity  = TIM_ICPolarity_Rising;  
    TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1; 
    TIM_ICInitStruct.TIM_ICFilter    = 0x0;
    TIM_ICInit(TIM1, &TIM_ICInitStruct);  
    TIM_ARRPreloadConfig(TIM1, ENABLE);
    TIM_SelectOnePulseMode(TIM1, TIM_OPMode_Single); 
    TIM_SelectInputTrigger(TIM1, TIM_TS_TI2FP2); 
    TIM_SelectSlaveMode(TIM1, TIM_SlaveMode_Trigger); 
    TIM_SetCounter(TIM1, 0);
    TIM_CtrlPWMOutputs(TIM1, ENABLE);
    TIM_Cmd(TIM1, ENABLE);
}

2.23 TIM3初始化

程序中调用TIM3_PWM_Init()函数配置TIM3的自动预装载值为(10000-1),预分频器的值为(SystemCoreClock / 1000000 - 1),即TIM3定时器发生更新事件的周期为20ms。

    TIM3_PWM_Init(10000 - 1, SystemCoreClock / 1000000 - 1);

在该函数中配置了TIM3_CH1输出PWM,输出信号极性为高电平有效,脉冲宽度为25%。详见函数定义如下:

    void TIM3_PWM_Init(u16 arr, u16 psc) 
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStruct;
    TIM_OCInitTypeDef  TIM_OCInitStruct;
    RCC_APB1PeriphClockCmd(RCC_APB1ENR_TIM3, ENABLE);
    TIM_TimeBaseStructInit(&TIM_TimeBaseStruct);
    TIM_TimeBaseStruct.TIM_Period = arr;
    TIM_TimeBaseStruct.TIM_Prescaler = psc;
    TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStruct);
    TIM_OCStructInit(&TIM_OCInitStruct);
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM2;
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStruct.TIM_Pulse = (arr / 4) * 3;
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; 
    TIM_OC1Init(TIM3, &TIM_OCInitStruct); 
    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); 
    TIM_ARRPreloadConfig(TIM3, ENABLE);  
    TIM_SetCounter(TIM3, 0);  
    TIM_Cmd(TIM3, ENABLE);
}

按照上述配置,PB4(TIM3_CH1)作为PA9(TIM1_CH2)的输入端,当PA9捕捉到PB4输出的PWM的上升沿时,TIM1计数器启动计数,控制延时为5ms,PA8输出一个脉宽为5ms的脉冲。

2.3 下载验证

连接PA9(TIM1_CH2)和PB4(TIM3_CH1),使用逻辑分析仪连接PA8和PA9,运行程序观测波形如下:

2.png

其中,通道8连接PA8,通道9连接PA9:

3.png

测量两段间距均为5ms,运行结果和上述配置一致。

4.png

来源:灵动MM32MCU

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

围观 34

祝贺灵动MM32F5270斩获 2022全球电子成就奖之“年度微控制器/接口”奖

1.jpg

11月10日,由全球电子技术领域知名媒体集团AspenCore主办的2022全球CEO峰会暨2022年度全球电子成就奖(WEAA, World Electronics Achievement Awards)颁奖典礼在深圳举办。灵动股份MM32F5270荣膺2022全球电子成就奖之“年度微控制器/接口”奖!

2.jpg

“全球电子成就奖”由AspenCore全球资深产业分析师组成的评审委员会以及来自亚、美、欧洲的网站用户群共同评选出得奖者。旨在聚焦领先科技,推动全球范围内的电子产业技术革新。获得全球电子成就奖的表彰,是一项备受认可的荣誉,充分体现了该技术或产品在业界的领先地位与不凡表现。

3.jpg

MM32F5270介绍

4.png

MM32F5270 是一款搭载了安谋科技 Arm China STAR-MC1 内核的 MCU 产品,其工作频率可达 120MHz,内置多达256KB Flash 和 192KB RAM,配置浮点运算单元(Floating Point Unit, FPU)、数字信号处理单元(Digital Signal Processing,DSP)、信号间互联矩阵 MindSwitch、可配置逻辑单元 CLU、三角函数加速单元 CORDIC 等高性能模块,并集成了丰富的外设和充足的 I/O 端口。MM32F5270 相较于现有产品全面提升了性能、存储容量、总线架构和外设配置,作为Cortex-M3/M4的理想升级之选,旨在覆盖更广泛的工业、汽车和 IoT 应用。

其主要性能包括:

  • Arm China STAR-MC1 处理器,基于 Armv8-M Mainline 指令集架构,集成FPU 和 DSP,单位频率 CoreMark 性能相较于 Cortex-M3 和 Cortex-M4 提升约 20%

  • 4KB L1 I-Cache,4KB L1 D-Cache

  • 多达 256KB 内置 Flash

  • 多达 192KB 内置 RAM(包括 32KB ITCM、32KB DTCM 和 128KB System RAM)

  • 内置 QSPI 接口,支持程序在线执行(eXecute-in-Place,XIP)

  • 内置 FSMC 并行存储器接口

  • 2 个 12 位 SAR ADC,采样率高达 3MSPS,配置最高 24 个外部通道,支持最高 256 倍硬件过采样

  • 2 个 12 位 DAC、3 个比较器

  • 7 个 16 位定时器、2 个 32 位定时器

  • 8 个 UART 接口(包含1 个 LPUART)、3 个 SPI 接口、3 个 I2S 接口、2 个 I2C 接口

  • 1 个 USB OTG 全速接口

  • 2 个 FlexCAN 接口

  • 部分型号配置 10M/100M 以太网控制器

  • 支持的温度范围为 -40℃ - 105 ℃

  • 提供 LQFP144、LQFP100 和 LQFP64封装

典型应用场景

主要包括:工业控制,电梯控制,消防控制,交通运输,打印机,扫描仪,家电控制,电机控制,扫地机器人等。

MM32F5270自推出以来就受到了市场的高度关注和客户群体的青睐,有关该产品的更多信息,请点击官方网站www.mm32mcu.com了解!

来源:灵动MM32MCU

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

围观 21

MM32F5270 系列控制器支持 I2S 总线接口,本章节在接下来会对 MM32F5270 I2S进行介绍,并使用 MM32F5270 和 CS4344 芯片进行 I2S 通信来演示播放一段声音。

I2S 简介

I2S ( Inter—IC Sound ) 总线是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准,该总线专责于音频设备之间的数据传输,广泛应用于各种多媒体系统。它采用了沿独立的导线传输时钟与数据信号的设计,通过将数据和时钟信号分离,避免了因时差诱发的失真,为用户节省了购买抵抗音频抖动的专业设备的费用。在飞利浦公司的 I2S 标准中,既规定了硬件接口规范,也规定了数字音频数据的格式。

在 MM32F5270 中,SPI 与 I2S 共用引脚。在 I2S 的描述中,支持半双工通信,也支持全双工模式。I2S 与 SPI 区别在于,I2S 主模式的控制逻辑使用独立的时钟分频处理单元。在半双工模式下,可使用 MCK 引脚输出驱动时钟,用于驱动外部音频组件。在全双工模式下,为了能同时进行数据的收发,MCK 驱动时钟只能从专用的 MCK 功能引脚输出。I2S 的功能框图如图 1 所示,包括时钟单元,寄存器控制单元,主从模式控制单元等。

“图1.
图1. I2S 功能框图

MM32F5270 中 I2S 主要特征

● 支持半双工通信(仅发射机或接收机)和全双工通信(SD,extSD 的数据方向根据主/从模式配置决定)两种通信方式

● 9 位可编程线性预分频器,以达到精确的音频采样频率( 8KHz 到 192KHz)

● 数据帧格式可以是 16 位、 24 位或 32 位

● 数据包帧固定为 16 位(16 位有效数据)或 32 位(16 位、 24 位、 32 位有效数据)

● 可编程时钟极性(稳定状态)

● 发射模式下的下溢标志(仅从机),接收模式下的上溢标志(主和从机)和接收/发射模式下的帧错误标志(仅从机)

● 用于传输和接收的 32 位寄存器为两个声道分时复用

● 支持 I2S 协议

▶ 飞利浦标准

▶ MSB 对齐标准(左对齐)

▶ LSB 对齐标准(右对齐)

▶ PCM 标准(在 16 位信道帧上具有短帧和长帧同步或扩展到 32 位信道帧的 16 位数据帧)

● 数据方向始终是 MSB 优先

● DMA 传输能力

● 可配置输出 MCLK 来驱动外部音频组件,比率固定在 256× FS(其中 FS 为音频采样频率)

I2S 信号接口

I2S 和 SPI 共用三个公共管脚:

● 串行时钟 CK

映射在 SCK 引脚上,也叫位时钟 BCLK,是主模式下的串行时钟输出以及从机模式下的串行时钟输入。SCLK 频率 = 2 x 采样频率 x 采样位数

● 帧时钟 WS

映射在 NSS 引脚上,是主模式下的串行时钟输出以及从机模式下的串行时钟输入。用于切换左右声道,LRCK 频率 = 采样频率

● 串行数据 SD

映射在 MOSI 管脚上,二进制补码表示的音频数据,用于发送或接收两次多路数据通道(仅在半双工模式下)。

● 当某些外部设备需要主时钟输入时,可以使用一个附加的管脚输出时钟到音频设备

● 主时钟 MCLK

映射在 MISO 引脚或专用 MCK 引脚上,当 I2S 配置为主模式时使用此时钟。MCLK 频率 = 256 x 采样频率 Fs

● I2S 引脚信号如图 2 所示。

“图2.
图2. I2S 引脚信号

数据格式

三线总线处理音频数据,必须经过分时复用两个声道:右声道和左声道。因为只有一个 32 位寄存器用于传输或接收,所以软件应依次配置寄存器 TXREG 为各声道的数据,或依次读取寄存器 RXREG 为各声道的数据。按照 I2S 协议,总是先发送左声道,然后发送右声道。

数据格式可以采用以下格式进行发送:

● 16 位数据打包在 16 位帧中

● 16 位数据打包在 32 位帧中

● 24 位数据打包在 32 位帧中

● 32 位数据打包在 32 位帧中

当使用 32 位帧上发送 16 位数据时,前 16 位(MSB)是有效的位,16 位 LSB 制为 0,无需任何软件操作,通过硬件实现,其他格式相似。

通信标准

I2S 接口支持四种音频标准,通过配置寄存器 SPI_I2S_I2SCFGR 中的 I2SSTD[1:0]、PCMSYNC 位进行切换;数据格式则通过配置 DATLEN[1:0]、CHLEN 来进行选择。对于所有通信标准及数据格式,总是先发送最高位(MSB 优先)。

● 飞利浦标准

对于飞利浦标准,WS 信号用于指示正在传输的声道。发射器在 CK 的下降沿锁存数据,接收器并在 CK 的上升读取数据。WS 信号也在 CK 的下降沿被锁定。对于这种标准 I2S 格式的信号,无论有多少位有效数据,数据的最高位总是出现在 WS 变化(也就是一帧开始)后的第 2 个 CK 脉冲处,如图 3 所示。

“图3.
图3. 飞利浦标准示意图

● MSB 对齐标准

对于 MSB 对齐标准,第一个数据在 WS 变化后的第一个沿有效,如图 4 所示。

“图4.
图4. MSB 对齐标准示意图

● LSB 对齐标准

对于 LSB 对齐标准,每个数据包帧的最低有效位总(LSB 位)是出现在 WS 变化前的 1 个 CK 脉冲周期处,如图 5 所示。

“图5.
图5. LSB 对齐标准示意图

● PCM 标准

对于 PCM 标准,不需要使用声道信息。PCM 有两个模式:短帧模式和长帧模式,通过配置SPI\_I2S\_I2SCFGR 寄存器的 PCMSYNC 位进行切换。在 PCM 模式下,输出信号(WS, SD)在 CK 信号的上升沿进行采样。输入信号(WS, SD)在 CK 下降沿被捕获。注意在主模式下, CK 和 WS 被配置为输出,如图 6 所示。

“图6.
图6. PCM 对齐标准示意图

I2S 配置

I2S 的配置主要包括主从模式选择,时钟极性,全双工半双工模式选择,采样频率和通信标准的配置,从而进行数据传输。

主模式

I2S 功能的主模式下,选择全双工工作时,SD 引脚作为主机发送接口,extSD 作为主机接收接口,SCK 和 WS作为主机的输出信号,同时 MCK 向外部提供可选的驱动时钟(配置 SPI_I2S_I2SCFGR.MCKOE 位为 ‘1’ 使能 MCK 输出),如图 7 所示。

“图7.
图7. I2S 主模式全双工通信

基于 MM32F5270 的声音播放实验

CS4344 芯片是实现本次实验功能的重要器件之一。CS4344 是一种立体声音频数模转换器 (DAC) ,可使用单个 +3.3 V 或 +5 V 电源,仅需要最小的支持电路。该系列线性模拟低通滤波器和自动速度模式检测,当自动选择 2 kHz 和 200 kHz 之间的采样率,使用采样率和主时钟速率方法。

本实验的基本原理是 MM32F3270 读取正弦波采样计算出的左右声道的数组数据,通过 I2S 接口将 PCM 信号传输给 CS4344,再经过 TS4871(音频功率放大器)连接到耳机接口,可以接入耳机等音频播放装置。

硬件设计

如图 8-9 是 PLUS-F5270 的 I2S 电路部分,完整原理图可以通过官网下载。其中,引脚信号对应分别为:

▶ I2S2_SD 对应于引脚 E6

▶ I2S2_CK 对应于引脚 D3

▶ I2S2_WS 对应于引脚 E4

▶ I2S2_MCK 对应于引脚 E5

“图8.
图8. I2S 电路图(1)

“图9.
图9. I2S 电路图(2)

GPIO 初始化 BOARD_InitPins()

配置 I2S 引脚,I2S_CK 为 PD3 引脚,I2S_SD 为 PE6 引脚,I2S_WS 为 PE4 引脚,I2S_MCK 为 PE5 引脚,复用通道为 AF5 。

void BOARD_InitPins(void)
{
    /* PB7 - UART1_TX. */
    GPIO_Init_Type gpio_init;
    gpio_init.Pins  = GPIO_PIN_6;
    gpio_init.PinMode  = GPIO_PinMode_AF_PushPull; //GPIO_PinMode_AF_PushPull
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &gpio_init);
    GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_7);

    /* PB6 - UART1_RX. */
    gpio_init.Pins  = GPIO_PIN_7;
    gpio_init.PinMode  = GPIO_PinMode_In_Floating; //GPIO_PinMode_In_Floating
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &gpio_init);
    GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_7);

    /* PD3 - I2S_CK. */
    gpio_init.Pins  = GPIO_PIN_3;
    gpio_init.PinMode  = GPIO_PinMode_AF_PushPull; //GPIO_PinMode_In_PushPull
    gpio_init.Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOD, &gpio_init);
    GPIO_PinAFConf(GPIOD, gpio_init.Pins, GPIO_AF_5);

    /* PE6 - I2S_SD. */
    gpio_init.Pins  = GPIO_PIN_6;
    gpio_init.PinMode  = GPIO_PinMode_AF_PushPull; //GPIO_PinMode_In_PushPull
    gpio_init.Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOE, &gpio_init);
    GPIO_PinAFConf(GPIOE, gpio_init.Pins, GPIO_AF_5);

    /* PE4 - I2S_WS. */
    gpio_init.Pins  = GPIO_PIN_4;
    gpio_init.PinMode  = GPIO_PinMode_AF_PushPull; //GPIO_PinMode_In_PushPull
    gpio_init.Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOE, &gpio_init);
    GPIO_PinAFConf(GPIOE, gpio_init.Pins, GPIO_AF_5);

    /* PE5 - I2S_MCK. */
    gpio_init.Pins  = GPIO_PIN_5;
    gpio_init.PinMode  = GPIO_PinMode_AF_PushPull; //GPIO_PinMode_In_PushPull
    gpio_init.Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOE, &gpio_init);
    GPIO_PinAFConf(GPIOE, gpio_init.Pins, GPIO_AF_5);
}

I2S 配置初始化 app_i2s_master_init()

初始化 I2S,配置时钟频率、采样率、数据长度、通信协议、传输模式及是否使能 MCLK 。

void app_i2s_master_init(void)
{
    /* setup I2S master module. */
    I2S_Master_Init_Type i2s_master_init;

    i2s_master_init.ClockFreqHz  = BOARD_I2S_FREQ;
    i2s_master_init.SampleRate   = BOARD_I2S_SAMPLE_RATE;
    i2s_master_init.DataWidth    = BOARD_I2S_DATA_WIDTH;
    i2s_master_init.Protocol     = BOARD_I2S_PROTOCOL;
    i2s_master_init.EnableMCLK   = true;
    i2s_master_init.Polarity     = BOARD_I2S_CPOL;
    i2s_master_init.XferMode     = I2S_XferMode_TxOnly;

    I2S_InitMaster(BOARD_I2S_PORT, &i2s_master_init);

    /* enable I2S. */
    I2S_Enable(BOARD_I2S_PORT, true);
}

● Protocol

通信标准格式选择,可选 I2S Philips 标准、左对齐标准、右对齐标准、 PCM 短帧标准或 PCM 长帧标准,它设定 SPI\_I2S\_I2SCFGR 寄存器 I2SSTD位和 PCMSYNC位的值。一般设置为 I2S Philips 标准即可。

● DataWidth

数据格式选择,设定有效数据长度和帧长度,可选标准 16bit 格式、扩展 16bit( 32bit 帧长度) 格式、 24bit 格式和 32bit 格式,它设定 SPI\_I2SCFGR 寄存器 DATLEN 位和CHLEN 位的值。对应 16bit 数据长度可选 16bit 或 32bit 帧长度,其他都是 32bit 帧长度。

● EnableMCLK

主时钟输出使能控制,可选使能输出或禁止输出,它设定 SPI_I2SPR 寄存器 MCKOE 位的值。为提高系统性能一般使能主时钟输出。

● SampleRate

采样频率设置,标准库提供采样采样频率选择,分别为 4KHz、8kHz、 11kHz、12KHz、16kHz、22kHz、32kHz、44kHz、48kHz、96kHz、192kHz 以及默认 2Hz,它设定 SPI\_I2S\_SPBRG 寄存器的值。

● Polarity

空闲状态的 CK 线电平,可选高电平或低电平,它设定 SPI\_I2S\_CCTL 寄存器 CPOL位的值。一般设置为低电平即可。

左右声道数据

采样函数 f (t) = A *sin( B*t ) , t 为采样时间,单位: s。采样位宽为 16bit,采样频率为 8 khz,系数 A 为10000,B 为2000π。

/* i2s left channel xfer data. */
const int16_t sound_buf_left[SOUND_BUF_SIZE] =
{
    0, 3535, 5000, 3535, 0, -3535, -5000, -3535,
    0, 3535, 5000, 3535, 0, -3535, -5000, -3535,
};

/* i2s right channel xfer data. */
const int16_t sound_buf_right[SOUND_BUF_SIZE] =
{
    0,  3827,  7071,  9238,  10000,  9238,  7071,  3827,
    0, -3827, -7071, -9238, -10000, -9238, -7071, -3827
};

左右声道数据传输

/* put data into left channel. */
void app_i2s_put_data_left(void)
{
    I2S_PutData(BOARD_I2S_PORT, (uint32_t)sound_buf_left[sound_buf_index_left]);
    sound_buf_index_left++;
    if (sound_buf_index_left >= SOUND_BUF_SIZE)
    {
        sound_buf_index_left = 0;
    }
}

/* put data into right channel. */
void app_i2s_put_data_right(void)
{
    I2S_PutData(BOARD_I2S_PORT, (uint32_t)sound_buf_right[sound_buf_index_right]);
    sound_buf_index_right++;
    if (sound_buf_index_right >= SOUND_BUF_SIZE)
    {
        sound_buf_index_right = 0;
    }
}

main() 函数

main() 函数结合上述操作,串口打印 "i2s_master_basic" ,初始化 I2S 后,将发送数组数据到左右声道缓冲区,进行声音播放。

int main(void)
{
    BOARD_Init();

    printf("i2s_master_basic.\r\n");

    app_i2s_master_init();

    while (1)
    {
        while(0u != (I2S_GetStatus(BOARD_I2S_PORT) & SPI_I2S_CSTAT_TXFULL_MASK) )
        {}

        app_i2s_put_data_left(); /* sending left channel data. */

        while(0u != (I2S_GetStatus(BOARD_I2S_PORT) & SPI_I2S_CSTAT_TXFULL_MASK) )
        {}
        app_i2s_put_data_right(); /* sending right channel data. */
    }
}

实验演示

本实验以搭载 MM32F5277E9PV 的 PLUS-F5270 开发板为平台,其扬声器将会播放左声道数据,演示视频如下:

演示1. 播放左声道数据

“

音响数据的采集、处理和传输是多媒体技术的重要组成部分。众多的数字音频系统已经进入消费市场,例如数字音频录音带、数字声音处理器。对于设备和生产厂家来说,标准化的信息传输结构可以提高系统的适应性。

本文介绍了 MM32F5270 中通过 I2S 和 CS4344 芯片进行通信来演示播放一段声音,后续将进行 MP3 的播放,未完待续!

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

围观 117
订阅 RSS - MM32F5270