Nucleo-F411RE (STM32F411)LL库体验 8 - PWM的使用

Nucleo-F411RE (STM32F411)LL库体验 8 - PWM的使用

1、简述

LD2连接PA5,而PA5可以映射TIM2_CH1,配合TIM2,可以输出PWM。
本片文章大量工作是添加了shell命令,可以通过pwm命令开关pwm以及设置pwm的频率,占空比等。
Nucleo-F411RE (STM32F411)LL库体验 8 - PWM的使用_第1张图片
Nucleo-F411RE (STM32F411)LL库体验 8 - PWM的使用_第2张图片

2、TIM2的初始化

prescaler以及period的计算,之前的文章里已经讲了很多了,不管gd32、mm32 ,stm32都是类似了。

__STATIC_INLINE void  BOARD_ConfigureTim2PwmOutput(void)
{
	/*************************/
	/* GPIO AF configuration */
	/*************************/
	/* Enable the peripheral clock of GPIOs */
	LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
	
	/* GPIO TIM2_CH1 configuration */
	LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_5, LL_GPIO_MODE_ALTERNATE);
	LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_5, LL_GPIO_PULL_DOWN);
	LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_5, LL_GPIO_SPEED_FREQ_HIGH);
	LL_GPIO_SetAFPin_0_7(GPIOA, LL_GPIO_PIN_5, LL_GPIO_AF_1);
	
	/***********************************************/
	/* Configure the NVIC to handle TIM2 interrupt */
	/***********************************************/
	NVIC_SetPriority(TIM2_IRQn, 0);
	NVIC_EnableIRQ(TIM2_IRQn);
	
	/******************************/
	/* Peripheral clocks enabling */
	/******************************/
	/* Enable the timer peripheral clock */
	LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2); 
	
	/***************************/
	/* Time base configuration */
	/***************************/
	/* Set counter mode */
	/* Reset value is LL_TIM_COUNTERMODE_UP */
	//LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);
	
	/* Set the pre-scaler value to have TIM2 counter clock equal to 10 kHz */
	LL_TIM_SetPrescaler(TIM2, __LL_TIM_CALC_PSC(SystemCoreClock, 1000000));
	
	/* Enable TIM2_ARR register preload. Writing to or reading from the         */
	/* auto-reload register accesses the preload register. The content of the   */
	/* preload register are transferred into the shadow register at each update */
	/* event (UEV).                                                             */  
	LL_TIM_EnableARRPreload(TIM2);
	
	/* Set the auto-reload value to have a counter frequency of 1000 Hz */
	/* TIM2CLK = SystemCoreClock / (APB prescaler & multiplier)               */

	LL_TIM_SetAutoReload(TIM2, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM2), 1000));
	
	/*********************************/
	/* Output waveform configuration */
	/*********************************/
	/* Set output mode */
	/* Reset value is LL_TIM_OCMODE_FROZEN */
	LL_TIM_OC_SetMode(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM1);
	
	/* Set output channel polarity */
	/* Reset value is LL_TIM_OCPOLARITY_HIGH */
	LL_TIM_OC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_OCPOLARITY_HIGH);
	
	/* Set compare value to half of the counter period (50% duty cycle ) */
	LL_TIM_OC_SetCompareCH1(TIM2, ( (LL_TIM_GetAutoReload(TIM2) + 1 ) / 2));
	
	/* Enable TIM2_CCR1 register preload. Read/Write operations access the      */
	/* preload register. TIM2_CCR1 preload value is loaded in the active        */
	/* at each update event.                                                    */
	LL_TIM_OC_EnablePreload(TIM2, LL_TIM_CHANNEL_CH1);
	
	/**************************/
	/* TIM2 interrupts set-up */
	/**************************/
	/* Enable the capture/compare interrupt for channel 1*/
	LL_TIM_EnableIT_CC1(TIM2);
	
	/**********************************/
	/* Start output signal generation */
	/**********************************/
	/* Enable output channel 1 */
	LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1);
	
	/* Enable counter */
	LL_TIM_EnableCounter(TIM2);
	
	/* Force update generation */
	LL_TIM_GenerateEvent_UPDATE(TIM2);
}

这边开了一个捕获中断,这里只是在捕获到高电平时,获取了一下当前的计数值,用来计算占空比。

void TIM2_IRQHandler(void)
{
	/* Check whether CC1 interrupt is pending */
	if(LL_TIM_IsActiveFlag_CC1(TIM2) == 1)
	{
		/* Clear the update interrupt flag*/
		LL_TIM_ClearFlag_CC1(TIM2);

		/* TIM2 capture/compare interrupt processing(function defined in main.c) */
		TimerCaptureCompare_Callback();
	}
}
uint16_t uwMeasuredDutyCycle;

void TimerCaptureCompare_Callback(void)
{
  	uwMeasuredDutyCycle = (LL_TIM_GetCounter(TIM2) * 100) / ( LL_TIM_GetAutoReload(TIM2) + 1 );
}

3、shell 命令添加。

pwm -i 获取系统信息
Nucleo-F411RE (STM32F411)LL库体验 8 - PWM的使用_第3张图片

pwm -h 命令帮助
Nucleo-F411RE (STM32F411)LL库体验 8 - PWM的使用_第4张图片

pwm -d xx 设置占空比
Nucleo-F411RE (STM32F411)LL库体验 8 - PWM的使用_第5张图片

pwm -f xx 设置pwm的频率
Nucleo-F411RE (STM32F411)LL库体验 8 - PWM的使用_第6张图片
pwm -p xx 设置tim的presclaer,设置后频率设置为1000HZ

Nucleo-F411RE (STM32F411)LL库体验 8 - PWM的使用_第7张图片
pwm -b 1开启呼吸灯,pwm -b 0关闭呼吸灯

命令代码:

#include "extend_shell.h"
#include "shell_port.h"
#include 
#include 
#include 
#include "getopt.h"
#include "main.h"

volatile bool isRunBreath = false;

const char *ocMode[] = {
    "LL_TIM_OCMODE_FROZEN",
    "LL_TIM_OCMODE_ACTIVE",
    "LL_TIM_OCMODE_INACTIVE",
    "LL_TIM_OCMODE_TOGGLE",
    "LL_TIM_OCMODE_FORCED_INACTIVE",
    "LL_TIM_OCMODE_FORCED_ACTIVE",
    "LL_TIM_OCMODE_PWM1",
    "LL_TIM_OCMODE_PWM2"
};

uint32_t TypeOcMode[8] = {LL_TIM_OCMODE_FROZEN,LL_TIM_OCMODE_ACTIVE,
                        LL_TIM_OCMODE_INACTIVE,LL_TIM_OCMODE_TOGGLE,
                        LL_TIM_OCMODE_FORCED_INACTIVE,LL_TIM_OCMODE_FORCED_ACTIVE,
                        LL_TIM_OCMODE_PWM1,LL_TIM_OCMODE_PWM2};

static const char * Find_OCModeName(uint32_t ocModeType)
{
    uint32_t i=0;
    for (i=0; i< 8;i++)
    {
        if (ocModeType == TypeOcMode[i])
            break;
    }
    return ocMode[i];
}                        

void PWM_ShowUsage(void)
{
    printf("Usage:\r\n");
    printf("  pwm (-h | --help)\r\n");
    printf("  pwm (-i | --info)\r\n");
    printf("  pwm (-r | --reload)       set pwm reload (0-65535)\r\n");
    printf("  pwm (-p | --prescaler)    set pwm prescaler (0-65535)\r\n");
    printf("  pwm (-d | --duty)         set pwm duty (1-100)\r\n");
    printf("  pwm (-e | --enable)       enable/disable pwm mode (0/1) \r\n");
    printf("  pwm (-b | --breath)       enable/disable led breath mode (0/1)\r\n");

}

void PWM_ShowInfomation(void)
{
    LL_RCC_ClocksTypeDef rccClock;
    LL_RCC_GetSystemClocksFreq(&rccClock);
    printf("Nucleo-F411RE Info:\r\n");
    printf(" Version                : %s %s\r\n",__DATE__,__TIME__);
    printf(" System Clock           : %ld\r\n",rccClock.SYSCLK_Frequency);
    printf(" AHB Clock              : %ld\r\n",rccClock.HCLK_Frequency);
    printf(" APB1 Clock             : %ld\r\n",rccClock.PCLK1_Frequency);
    printf(" APB2 Clock             : %ld\r\n",rccClock.PCLK2_Frequency);
    printf(" TIM2 Status            : %s\r\n",LL_TIM_IsEnabledCounter(TIM2)==1?"enable" : "disable");
    printf(" TIM2 OC Mode           : %s\r\n",Find_OCModeName(LL_TIM_OC_GetMode(TIM2,LL_TIM_CHANNEL_CH1)));
    printf(" TIM2 CHannel Status    : %s\r\n",LL_TIM_CC_IsEnabledChannel(TIM2,LL_TIM_CHANNEL_CH1) ==1 ?"enable": "disable");
    printf(" TIM2 PWM Prescaler     : %ld\r\n",LL_TIM_GetPrescaler(TIM2) + 1);
    printf(" TIM2 PWM AutoReload    : %ld\r\n",LL_TIM_GetAutoReload(TIM2) + 1);
    printf(" TIM2 PWM Duty          : %.1f%%\r\n",(float)(LL_TIM_OC_GetCompareCH1(TIM2)* 100)/(LL_TIM_GetAutoReload(TIM2) + 1));
    printf(" TIM2 PWM Freq          : %ldHZ\r\n",(rccClock.SYSCLK_Frequency)/((LL_TIM_GetPrescaler(TIM2) + 1)*(LL_TIM_GetAutoReload(TIM2) + 1)));
}

int PWM_Control(int argc,char *argv[])
{       
    int c;
    int longindex = 0;
    int duty = 0;
    int breath = 0;
    const char short_options[] = "hid:e:b:p:f:";
    const struct option long_options[] =
    {
        {"help",            0,  NULL,     'h'},
        {"info",            0,  NULL,     'i'},
        {"duty",            1,  NULL,     'd'},
        {"enable",          1,  NULL,     'e'},
        {"breath",          1,  NULL,     'b'},
        {"freq",            1,  NULL,     'f'},
        {"prescaler",       1,  NULL,     'p'},
        {NULL,              0,  NULL,     0},
    };

    if (argc == 1)
    {
        /* goto the help */
        PWM_ShowUsage();
        return 0;
    }
   /* init 0 */
    optind = 0;
    opterr = 0;
    /* parse */
    do
    {
        /* parse the args */
        c = getopt_long(argc, argv, short_options, long_options, &longindex);
        switch (c)
        {
        case 'f':
            printf("Now set pwm freq %s\r\n",optarg);
            int freq = strtol(optarg,NULL,10);
            if (freq > 0)
            {
                int reload = __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM2), freq);
                if (reload + 1 > 65535)
                {
                    printf("auto reload value must 0-65535,please set prscaler first\r\n");
                    return 0;
                }
                else
                {
                    LL_TIM_DisableCounter(TIM2);
                    LL_TIM_SetAutoReload(TIM2, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM2), freq));
                    LL_TIM_OC_SetCompareCH1(TIM2, ( (LL_TIM_GetAutoReload(TIM2) + 1 ) / 2));
                    LL_TIM_EnableCounter(TIM2);
                }
            }
            else
            {
                printf(" set freq error ,must pwm -f xx\r\n");
                return 0;
            }
            break;    
        case 'p':
            printf("Now set pwm prescaler %s\r\n",optarg);
            int prescaler = strtol(optarg,NULL,10);
            if (prescaler > 0 && (prescaler) < 65535)
            {
                LL_TIM_DisableCounter(TIM2);
                LL_TIM_SetPrescaler(TIM2,prescaler-1);
                int reload = __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM2), 1000); //修改perscaler 默认1000HZ
                if (reload + 1 > 65535)
                {
                    printf("reload value must 0-65525\r\n");
                    return 0;
                }
                else
                {
                    LL_TIM_SetAutoReload(TIM2,reload);
                    LL_TIM_OC_SetCompareCH1(TIM2, ( (LL_TIM_GetAutoReload(TIM2) + 1 ) / 2));
                    LL_TIM_EnableCounter(TIM2);
                }
            }
            else
            {
                printf(" set prescaler error ,must pwm -f xx\r\n");
                return 0;
            }

            break;
        case 'h'/* constant-expression */:
            /* code */
            PWM_ShowUsage();
            return 0;
        case 'i':
            PWM_ShowInfomation();
            break;
        case 'd': 
            
            duty = strtol(optarg,NULL,10);
            printf("Now set pwm duty %s\r\n",optarg);
            if (duty >= 0 && duty <=100)
            {
                duty = (duty  * (LL_TIM_GetAutoReload(TIM2) + 1)) / 100;

                LL_TIM_OC_SetCompareCH1(TIM2, duty);
            }
            else
            {
                printf(" set duty error,must between 0 and 100\r\n");
                return 0;
            }
            
            break;
        case 'e': 
            printf("Now set pwm state\r\n");

            break;
        case 'b': 
            breath = strtol(optarg,NULL,10);
            if (breath == 1)
            {
                isRunBreath = true;
            }
            else if (breath == 0)
            {
                isRunBreath = false;
            }
            else
            {
                printf(" error ,must be 0/1\r\n");
            }

            printf("Now set pwm breath\r\n");
            break;
        default:
            break;
        }
        
    }while (c != -1);

    return 0;
}

SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), pwm, PWM_Control, PWM_Control);

代码

代码下载

你可能感兴趣的:(Nucleo-F411RE,stm32,单片机,嵌入式硬件)