stm32之PWM呼吸灯

        呼吸灯是灯从渐亮到渐灭周而复始形成的一个效果。由于51没有PWM所以需要定时器模拟PWM才能实现呼吸灯的效果,但是stm32的通用定时器是有PWM模式的,所以不需要再用软件模拟,精准度也高。

本实验用的基于stm32f103C8t6。在PB8引脚上接了一个led, led的另一端接到vcc上。

PB8除了是一个GPIO功能,还有一个复用功能即定时器4的channel 3功能。可以通过参考手册知晓。

一、利用CubeMX生成代码

具体配置就不细说了,这里将TIM4的关键配置标了出来

记得选中PWM 的模式1 和使能比较输出,CH Polarity设置Low 和 High 在呼吸灯这里无影响。

1.1、计数器配置

时钟的溢出配置公式如下:

这里将定时器设置为500ms,即Tout = 500ms,同时PSC = 71,ARR = 499, Tclk = 72MHZ。根据公式计算出Tout = (71 + 1) * (499 + 1) / 72000000 = 500ms。

1.2、main函数代码配置

int main(void)
{uint16_t pwmVal = 0;uint8_t dir = 1;/* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* Configure the system clock */SystemClock_Config();/* Initialize all configured peripherals */MX_GPIO_Init();MX_TIM4_Init();/* USER CODE BEGIN 2 */	// 开启定时器4HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_3);while (1){HAL_Delay(1);if(dir) {pwmVal ++;} else {pwmVal--;}if(pwmVal > 500) {dir = 0;} else if(pwmVal <= 0) {dir = 1;}__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, pwmVal);
/*		// 常亮__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, 0); // 常灭__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, 500); */}
}

二、PWM分析

        输出比较就是通过定时器的外部引脚对外输出控制信号,有八种模式,由寄存器 CCMRx 的位 OCxM[2:0]控制。
  1. 000:冻结。输出比较寄存器TIMx_CCR1与计数器TIMx_CNT间的比较对OC1REF不起作用;
  2. 001 :匹配时设置通道 1 为有效电平。当计数器 TIMx_CNT 的值与TIMx_CCR1相同时,强制OC1REF为高。
  3. 010 :匹配时设置通道 1 为无效电平。当计数器 TIMx_CNT 的值与TIMx_CCR1相同时,强制OC1REF为低。
  4. 011:翻转。当TIMx_CCR1=TIMx_CNT时,翻转OC1REF的电平。
  5. 100:强制为无效电平。强制OC1REF为低。
  6. 101:强制为有效电平。强制OC1REF为高。
  7. 110PWM模式1- 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电(OC1REF=0),否则为有效电平(OC1REF=1)
  8. 111PWM模式2- 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为无效电平,否则为有效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为有效电平,否则为无效电平。
其中 PWM 模式是输出比较中的特例,使用的也最多。在PWM的模式1或2下,会一直进行 TIMx_CNT和TIMx_CCRx的比较。
  • PWM中GPIO引脚电平输出是由OCx来决定的而不是由OCxREF来决定的。
  • 正常GPIO的引脚电平输出由寄存器ODR来决定的(可以配置BSRR来决定ODR的输出)

下图是捕获/比较的输出阶段:

根据上图可以推出四种结果分别是

2.1、有效电平

PWM模式1

  • 在向上计数时,一旦TIMx_CNT < TIMx_CCR1时通道1为有效电平,否则为无效电平;
  • 在向下计数时,一旦TIMx_CNT > TIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)
  • TIMx_CCRx > TIMx_ARR时 OCxREF = 1
  • TIMx_CCRx = 0时OCxREF = 0

PWM模式2

  • 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为无效电平,否则为有效电平
  • 在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为有效电平,否则为无效电

上图中绿框部分是有效的电平。这里有有效电平是OCxREF 参考电平。

OCx有效电平

手册中还有另外一个描述就是:

The output stage generates an intermediate waveform which is then used for reference:
OCxRef (active high). The polarity acts at the end of the chain.

翻译一下就是:

输出部分产生一个中间波形OCxRef(高有效)作为基准,链的末端决定最终输出信号的极性。

“链的末端决定最终输出信号的极性” 怎么解释,这个可以通过CCIP位的说明可以看出来。
CC1P : Capture/Compare 1 output polarity
CC1 channel configured as output:
  • 0: OC1 active high.
  • 1: OC1 active low.

CC1通道作为输出模式

  • 当CCIP = 0时,OC1 = 1是有效电平,
  • 当CCIP = 1时,OC1 = 0是有效电平

2.2、PWM功能下GPIO引脚输出电平

在参考文档中有这样一个表格:

CCxE = 0时,禁止OCx输出,CCxE = 1时,OCx = OCxREF + Polarity

这里的OCxREF + Polarity是什么意思。这里先说明下这里是xor(异或)的意思。

我们可以从以下分析出:

在参考文档中的TIM1定时器章节有这样一个表格:

红色框中圈住的部分写出了OCx = OCxREF xor CCxP,当然这个表格是在TIM1和TIM8里出现的,像表格中的MOE,OSSI,OSSR,CCxNE,都是在TIM1和TIM8寄存器中存在的,在通用定时器里是没有的。

MOE,OSSI,OSSR存在于TIM1和TIM8寄存器中的BDTR,CCxNP和CCxNE也只存在于TIM1和TIM8定时器中的CCER寄存器。

TIM1和TIM8中的CCER

通用定时器中的CCER (reserved部分要保持为0,即保持reset时的值)

在通用定时器里面,OSSR 无效, CCxNE = 0, OSSI无效,MOE 无效,所以异或操作还是适用的。

  • 当CCIP = 0时,OC1 = 1是有效电平,
  • 当CCIP = 1时,OC1 = 0是有效电平

和 OCx = OCxREF xor CCxP

得出以下最终结果(绿色部分为有效输出):

总结:

  • 1、PWM的模式用来区分有效电平在哪个区间输出 ,并不能区分是有效电平是高还是低
  • 2、有效的电平的输出OCx由CCER寄存器的CCxP位来决定。
  • 3、CCxP = 0时(默认),输出与OCxREF相同的波
  • 4、CCxP = 1时,输出与OCxREF相反的波

三、代码分析

PWM的主要流程大致如下:

  1. 初始化TIM4
  2. 开启TIM4的PWM模式
  3. 设置CCR1用于动态配置PWM波形的输出

 代码主要是根据 定时器4的channel 3 + 向上计数模式 + 500ms 定时周期 这个为中心产生的。定时器涉及的寄存器比较多,定时总共有20种寄存器,在PWM输出模式下,用到的其实并不多。涉及的寄存器如下:

CR1 (control register)

CR2 (control register)

SMCR (slave mode control register)

EGR (event generation register)

 CCMR (capture/compare mode register 2 )

CCER (capture/compare enable register )

下面三个主要用来装载数据和配置无关

CNT (counter)

ARR (auto-reload register )

CCR3 (capture/compare register 3 )

3.1、MX_TIM4_Init

函数比较长,大致将功能分了下类,具体函数如下:

void MX_TIM4_Init(void)
{// 这里主要是根据功能将寄存器分成几个模块进行配置// 时钟相关配置TIM_ClockConfigTypeDef sClockSourceConfig = {0};// 主从模式配置TIM_MasterConfigTypeDef sMasterConfig = {0};// 定时器输出捕获常规配置TIM_OC_InitTypeDef sConfigOC = {0};htim4.Instance = TIM4;htim4.Init.Prescaler = 71;htim4.Init.CounterMode = TIM_COUNTERMODE_UP;htim4.Init.Period = 499;htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;// 1、定时器常规初始化if (HAL_TIM_Base_Init(&htim4) != HAL_OK){Error_Handler();}// 2、定时器时钟配置sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK){Error_Handler();}// 3、定时器PWM初始化if (HAL_TIM_PWM_Init(&htim4) != HAL_OK){Error_Handler();}// 4、定时器主从模式配置sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK){Error_Handler();}sConfigOC.OCMode = TIM_OCMODE_PWM1;sConfigOC.Pulse = 0;sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;// 5、定时器channel配置if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_3) != HAL_OK){Error_Handler();}// 6、定时器主栈地址初始化HAL_TIM_MspPostInit(&htim4);}

上面主要做了下面几件事

  • 1、定时器常规初始化(时基单元相关)
  • 2、定时器时钟配置
  • 3、定时器PWM初始化
  • 4、定时器主从模式配置
  • 5、定时器channel配置
  • 6、定时器主栈地址初始化(实际上就是使能定时器)

下面主要针对上面的过程进行描述

3.1.1、定时器相关类型

TIM_TypeDef

typedef struct
{__IO uint32_t CR1;             /*!< TIM control register 1,                      Address offset: 0x00 */__IO uint32_t CR2;             /*!< TIM control register 2,                      Address offset: 0x04 */__IO uint32_t SMCR;            /*!< TIM slave Mode Control register,             Address offset: 0x08 */__IO uint32_t DIER;            /*!< TIM DMA/interrupt enable register,           Address offset: 0x0C */__IO uint32_t SR;              /*!< TIM status register,                         Address offset: 0x10 */__IO uint32_t EGR;             /*!< TIM event generation register,               Address offset: 0x14 */__IO uint32_t CCMR1;           /*!< TIM  capture/compare mode register 1,        Address offset: 0x18 */__IO uint32_t CCMR2;           /*!< TIM  capture/compare mode register 2,        Address offset: 0x1C */__IO uint32_t CCER;            /*!< TIM capture/compare enable register,         Address offset: 0x20 */__IO uint32_t CNT;             /*!< TIM counter register,                        Address offset: 0x24 */__IO uint32_t PSC;             /*!< TIM prescaler register,                      Address offset: 0x28 */__IO uint32_t ARR;             /*!< TIM auto-reload register,                    Address offset: 0x2C */__IO uint32_t RCR;             /*!< TIM  repetition counter register,            Address offset: 0x30 */__IO uint32_t CCR1;            /*!< TIM capture/compare register 1,              Address offset: 0x34 */__IO uint32_t CCR2;            /*!< TIM capture/compare register 2,              Address offset: 0x38 */__IO uint32_t CCR3;            /*!< TIM capture/compare register 3,              Address offset: 0x3C */__IO uint32_t CCR4;            /*!< TIM capture/compare register 4,              Address offset: 0x40 */__IO uint32_t BDTR;            /*!< TIM break and dead-time register,            Address offset: 0x44 */__IO uint32_t DCR;             /*!< TIM DMA control register,                    Address offset: 0x48 */__IO uint32_t DMAR;            /*!< TIM DMA address for full transfer register,  Address offset: 0x4C */__IO uint32_t OR;              /*!< TIM option register,                         Address offset: 0x50 */
}TIM_TypeDef;

TIM_TypeDef 结构体包括了定时器所有的寄存器,通过操作结构体就可以操作寄存器。在初始化的时候有这样一句代码 htim4.Instance = TIM4 这里的TIM4就是定时器4在外设中的地址,TIM4也是一个宏,具体就不展开了,它的定义和 GPIO类似,可参考GPIO,或自行在代码中查看。

TIM_HandleTypeDef

typedef struct
{uint32_t Prescaler;        // 配置时基单元中的预分频器    uint32_t CounterMode;      // 计数模式(向上/向下/中央对齐)uint32_t Period;           // 定时周期(period + 1)uint32_t ClockDivision;    // 时钟分频因子uint32_t RepetitionCounter; // 重复定时器(高级定时器中用)uint32_t AutoReloadPreload; // 是否自动重装初值
} TIM_Base_InitTypeDef;

HAL_TIM_ActiveChannel (选中的channel)

typedef enum
{HAL_TIM_ACTIVE_CHANNEL_1        = 0x01U,    /*!< The active channel is 1     */HAL_TIM_ACTIVE_CHANNEL_2        = 0x02U,    /*!< The active channel is 2     */HAL_TIM_ACTIVE_CHANNEL_3        = 0x04U,    /*!< The active channel is 3     */HAL_TIM_ACTIVE_CHANNEL_4        = 0x08U,    /*!< The active channel is 4     */HAL_TIM_ACTIVE_CHANNEL_CLEARED  = 0x00U     /*!< All active channels cleared */
} HAL_TIM_ActiveChannel;

TIM_HandleTypeDef(保存定时器相关配置,状态和方法,下面进行了精简)

{TIM_TypeDef                        *Instance;  // 定时器寄存器集合       TIM_Base_InitTypeDef               Init;       // 定时器基本配置       HAL_TIM_ActiveChannel              Channel;     //使用的channelDMA_HandleTypeDef                  *hdma[7];          HAL_LockTypeDef                    Lock;          //是否进行锁定,配置完成之后都要进行锁定    __IO HAL_TIM_StateTypeDef          State;          //定时器状态   __IO HAL_TIM_ChannelStateTypeDef   ChannelState[4];   // channel的状态,总共有四个channel__IO HAL_TIM_ChannelStateTypeDef   ChannelNState[4];  __IO HAL_TIM_DMABurstStateTypeDef  DMABurstState;     // 函数指针就写了一个,其它的看源码哈void (* Base_MspInitCallback)(struct __TIM_HandleTypeDef *htim); 
} TIM_HandleTypeDef;
3.1.2、定时器常规初始化
下面的代码也进行了精简,方便看主要的过程。
HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim)
{/* Check the TIM handle allocation */if (htim == NULL){return HAL_ERROR;}// 结构体初始化时未设置,默认是RESET状态if (htim->State == HAL_TIM_STATE_RESET){htim->Lock = HAL_UNLOCKED;// 由于未注册定时器回调,这里把回调相关的方法删除了HAL_TIM_Base_MspInit(htim);}// 设置busy状态,防止操作定时器htim->State = HAL_TIM_STATE_BUSY;// 将Init中的配置同步到Tim4的寄存器中TIM_Base_SetConfig(htim->Instance, &htim->Init);// 未涉及DMAhtim->DMABurstState = HAL_DMA_BURST_STATE_READY;//将四个channel设置成readyTIM_CHANNEL_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY);//将四个互补channel设置成ready(暂时无用)TIM_CHANNEL_N_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY);// 设置就绪状态htim->State = HAL_TIM_STATE_READY;return HAL_OK;
}

初始化核心代码是TIM_Base_SetConfig这个函数,具体实现如下

void TIM_Base_SetConfig(TIM_TypeDef *TIMx, const TIM_Base_InitTypeDef *Structure)
{uint32_t tmpcr1;tmpcr1 = TIMx->CR1;// 只要是TIM1-4 就会成立(这是一个简单的宏)if (IS_TIM_COUNTER_MODE_SELECT_INSTANCE(TIMx)){// 清除CR1寄存器中的DIR 和CMS位// DIR是CR1中的第4位,CMS是5 6 位// DIR = 10000b  CMS = 1100000// 下面的意思是将DIR和CMS清0tmpcr1 &= ~(TIM_CR1_DIR | TIM_CR1_CMS);// 重新设置计数模式, 这里只设置了DIR,CMS保持00,00状态就是边沿对齐模式(向上或向下)// 下面的意思就是设置了边沿对齐的向上计数模式tmpcr1 |= Structure->CounterMode;}// 只要是TIM1-4 就会成立(这是一个简单的宏)if (IS_TIM_CLOCK_DIVISION_INSTANCE(TIMx)){// 清除时钟分频因子TIM_CR1_CKD = 1100000000b,下面就是清除CKDtmpcr1 &= ~TIM_CR1_CKD;// 重新配置时钟分频因子,本安全中外部传入的是0tmpcr1 |= (uint32_t)Structure->ClockDivision;}// 这里先清除CR1中ARPE位,然后根据AutoReloadPreload配置,就是是否使能自动重装初值MODIFY_REG(tmpcr1, TIM_CR1_ARPE, Structure->AutoReloadPreload);// 配置CR1 寄存器TIMx->CR1 = tmpcr1;// 配置ARR自动重装寄存器TIMx->ARR = (uint32_t)Structure->Period ;// 配置PSC寄存器TIMx->PSC = Structure->Prescaler;//TIM1 才有效if (IS_TIM_REPETITION_COUNTER_INSTANCE(TIMx)){/* Set the Repetition Counter value */TIMx->RCR = Structure->RepetitionCounter;}// 配置事件产生寄存器UG代码第0位,数值1代表定时器溢出时会产生更新事件TIMx->EGR = TIM_EGR_UG;
}

上面的代码主要是设置CR1、ARR、PSC、EGR和RCR(TIM1才有效)寄存器。

3.1.3、定时器时钟配置

项目中用到的是内部时钟,所以代码简化如下,这个函数主要处理SMCR寄存器的配置。

HAL_StatusTypeDef HAL_TIM_ConfigClockSource(TIM_HandleTypeDef *htim, const TIM_ClockConfigTypeDef *sClockSourceConfig)
{HAL_StatusTypeDef status = HAL_OK;uint32_t tmpsmcr;__HAL_LOCK(htim);htim->State = HAL_TIM_STATE_BUSY;tmpsmcr = htim->Instance->SMCR;// 下面的意思重置从模式寄存器所有位除了MSM位,tmpsmcr &= ~(TIM_SMCR_SMS | TIM_SMCR_TS);tmpsmcr &= ~(TIM_SMCR_ETF | TIM_SMCR_ETPS | TIM_SMCR_ECE | TIM_SMCR_ETP);htim->Instance->SMCR = tmpsmcr;htim->State = HAL_TIM_STATE_READY;__HAL_UNLOCK(htim);return status;
}
3.1.4、定时器PWM初始化

由于未开启PWM回调, 这里的操作和定时器常规初始化几乎一样

HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim)
{if (htim->State == HAL_TIM_STATE_RESET){htim->Lock = HAL_UNLOCKED;// 这里的条件不成立
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)/* Reset interrupt callbacks to legacy weak callbacks */TIM_ResetCallback(htim);if (htim->PWM_MspInitCallback == NULL){htim->PWM_MspInitCallback = HAL_TIM_PWM_MspInit;}htim->PWM_MspInitCallback(htim);
#else/* Init the low level hardware : GPIO, CLOCK, NVIC and DMA */// 这里是一个空操作HAL_TIM_PWM_MspInit(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */}/* Set the TIM state */htim->State = HAL_TIM_STATE_BUSY;// 重新走了一下定时器的配置TIM_Base_SetConfig(htim->Instance, &htim->Init);htim->DMABurstState = HAL_DMA_BURST_STATE_READY;TIM_CHANNEL_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY);TIM_CHANNEL_N_STATE_SET_ALL(htim, HAL_TIM_CHANNEL_STATE_READY);htim->State = HAL_TIM_STATE_READY;return HAL_OK;
}
3.1.5、定时器主从模式配置

代表也非常简单,大致如下:

HAL_StatusTypeDef HAL_TIMEx_MasterConfigSynchronization(TIM_HandleTypeDef *htim,const TIM_MasterConfigTypeDef *sMasterConfig)
{uint32_t tmpcr2;uint32_t tmpsmcr;__HAL_LOCK(htim);htim->State = HAL_TIM_STATE_BUSY;tmpcr2 = htim->Instance->CR2;tmpsmcr = htim->Instance->SMCR;// 清除CR2寄存器中的MMS位,即 4 5 6 都是0tmpcr2 &= ~TIM_CR2_MMS;// 设置新的MMS主模式选择tmpcr2 |=  sMasterConfig->MasterOutputTrigger;// 将CR2配置到寄存器中htim->Instance->CR2 = tmpcr2;if (IS_TIM_SLAVE_INSTANCE(htim->Instance)){// 清除SMCR中的msm(主从模式选择)tmpsmcr &= ~TIM_SMCR_MSM;// 外部传入的是DISABLE	= 0,0代表无作用tmpsmcr |= sMasterConfig->MasterSlaveMode;// 设置回寄存器htim->Instance->SMCR = tmpsmcr;}htim->State = HAL_TIM_STATE_READY;__HAL_UNLOCK(htim);return HAL_OK;
}
3.1.6、定时器主从模式配置

选调用 HAL_TIM_PWM_ConfigChannel,内部对channel3的处理如下

TIM_OC3_SetConfig(htim->Instance, sConfig);/* Set the Preload enable bit for channel3 */htim->Instance->CCMR2 |= TIM_CCMR2_OC3PE;/* Configure the Output Fast mode */htim->Instance->CCMR2 &= ~TIM_CCMR2_OC3FE;htim->Instance->CCMR2 |= sConfig->OCFastMode;break;

核心代码 是TIM_OC3_SetConfig函数

static void TIM_OC3_SetConfig(TIM_TypeDef *TIMx, const TIM_OC_InitTypeDef *OC_Config)
{uint32_t tmpccmrx;uint32_t tmpccer;uint32_t tmpcr2;tmpccer = TIMx->CCER;// 清除CCE使能位TIMx->CCER &= ~TIM_CCER_CC3E;tmpcr2 =  TIMx->CR2;tmpccmrx = TIMx->CCMR2;// 清除CCMR2(输入捕获寄存器)0C3M(输出比较3模式),CC3S(捕获比较3选择)tmpccmrx &= ~TIM_CCMR2_OC3M;tmpccmrx &= ~TIM_CCMR2_CC3S;// 外部设置的 TIM_OCMODE_PWM1即110 0000(向上计数模式)tmpccmrx |= OC_Config->OCMode;// 清除CCER(输入捕获寄存器)极性位tmpccer &= ~TIM_CCER_CC3P;// 外部传入的HIGH = 0,CC3E = 0 禁止输出tmpccer |= (OC_Config->OCPolarity << 8U);/* Write to TIMx CR2 */TIMx->CR2 = tmpcr2;/* Write to TIMx CCMR2 */TIMx->CCMR2 = tmpccmrx;/* Set the Capture Compare Register value */TIMx->CCR3 = OC_Config->Pulse;/* Write to TIMx CCER */TIMx->CCER = tmpccer;
}

3.1.7、主栈地址初始化

里面就是就是使能了一下timer定时器

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{if(tim_baseHandle->Instance==TIM4){/* TIM4 clock enable */__HAL_RCC_TIM4_CLK_ENABLE();}
}

3.2、开启TIM4的PWM模式

HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
{uint32_t tmpsmcr;/* Check the parameters */assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));/* Check the TIM channel state */if (TIM_CHANNEL_STATE_GET(htim, Channel) != HAL_TIM_CHANNEL_STATE_READY){return HAL_ERROR;}// 将channel设置成busy(这里传入的是channel3)TIM_CHANNEL_STATE_SET(htim, Channel, HAL_TIM_CHANNEL_STATE_BUSY);// 使能channel 3TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);// TIM1才会进if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET){/* Enable the main output */__HAL_TIM_MOE_ENABLE(htim);}/* Enable the Peripheral, except in trigger mode where enable is automatically done with trigger */if (IS_TIM_SLAVE_INSTANCE(htim->Instance)){// 获取SMCR 中0-2位(SMS) ,外部SMS是关闭的即0tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;// 这个比较不成功 000 != 110if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr)){// 使能CR1的第0位CEN开启计数__HAL_TIM_ENABLE(htim);}}else{__HAL_TIM_ENABLE(htim);}/* Return function status */return HAL_OK;
}

核心代码就是使能通道3

void TIM_CCxChannelCmd(TIM_TypeDef *TIMx, uint32_t Channel, uint32_t ChannelState)
{uint32_t tmp;/* Check the parameters */assert_param(IS_TIM_CC1_INSTANCE(TIMx));assert_param(IS_TIM_CHANNELS(Channel));// 外部是Channel3 = 1000 ,Channel & 0x1FU = 1000,tmp = TIM_CCER_CC1E << (Channel & 0x1FU); /* 0x1FU = 31 bits max shift */// 清除CC3ETIMx->CCER &= ~tmp;// 这里ChannelState = Enable = 1, 使能CC3ETIMx->CCER |= (uint32_t)(ChannelState << (Channel & 0x1FU)); /* 0x1FU = 31 bits max shift */
}

3.3、设置CCR1用于动态配置PWM波形的输出

更改CCR3,来设置占空比。

#define __HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__) \(((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCR1 = (__COMPARE__)) :\((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCR2 = (__COMPARE__)) :\((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCR3 = (__COMPARE__)) :\((__HANDLE__)->Instance->CCR4 = (__COMPARE__)))#define __HAL_TIM_SetCompare            __HAL_TIM_SET_COMPARE__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, pwmVal);

四、总结

1、HAL 中每次在设置相应的位时都会先清除一下,清除时设置的宏对应位是1。

tmpcr1 &= ~TIM_CR1_CKD;
tmpcr1 |= (uint32_t)Structure->ClockDivision;

2、定时器单线程初始化时通常会加锁,完成之后解锁(别忘解锁)。

 __HAL_LOCK(htim);__HAL_UNLOCK(htim);// 加锁时会判断有没有锁住,没有锁住再加锁,有锁就直接返回
#define __HAL_LOCK(__HANDLE__)                                           \do{                                        \if((__HANDLE__)->Lock == HAL_LOCKED)   \{                                      \return HAL_BUSY;                    \}                                      \else                                   \{                                      \(__HANDLE__)->Lock = HAL_LOCKED;    \}                                      \}while (0U)
// 返回时直接解锁
#define __HAL_UNLOCK(__HANDLE__)                                          \do{                                       \(__HANDLE__)->Lock = HAL_UNLOCKED;    \}while (0U)

代码地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/139644.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

rabbitMQ (1)

文章目录 1. RabbitMQ 介绍1.1 几个重要概念1.2 RabbitMq 的工作原理 2 RabbitMQ 安装3. RabbitMQ 入门操作3.1 添加依赖3.2 生产者代码3.3 消费者代码 4. Work Queues5. 管理端页面创建队列 1. RabbitMQ 介绍 引用 &#xff1a; RabbitMQ 是一个消息中间件&#xff1a;它接受…

扩展pytest接口自动化框架-MS数据解析功能

【软件测试行业现状】2023年了你还敢学软件测试&#xff1f;未来已寄..测试人该何去何从&#xff1f;【自动化测试、测试开发、性能测试】 开篇 MeterSphere的数据源通过html页面上传后&#xff0c;需要将请求方式进行拆分。 get接口的参数&#xff0c;常以params的方式进行传…

arcgis js 缓冲区分析(GP服务)

arcgis文档中的有提供缓冲区的接口 geometryService&#xff0c;但要4.19后版本才提供 案例中使用的版本为4.16&#xff0c;因此这里的缓冲区分析借助gp工具 新建服务 1、打开arcmap 选择工具将要存放的文件夹&#xff0c;右键> new > Toolbox 对新建好的工具的mode…

343. 整数拆分

题目&#xff1a; 343. 整数拆分 给定一个正整数 n &#xff0c;将其拆分为 k 个 正整数 的和&#xff08; k > 2 &#xff09;&#xff0c;并使这些整数的乘积最大化。 返回 你可以获得的最大乘积 。 示例 1: 输入: n 2 输出: 1 解释: 2 1 1, 1 1 1。 示例 2: 输…

spring:实现初始化动态bean|获取对象型数组配置文件

0. 引言 近期因为要完成实现中间件的工具包组件&#xff0c;其中涉及要读取对象型的数组配置文件&#xff0c;并且还要将其加载为bean&#xff0c;因为使用了spring 4.3.25.RELEASE版本&#xff0c;很多springboot的相关特性无法支持&#xff0c;因此特此记录&#xff0c;以方…

Springboot2 Pandas Pyecharts 量子科技专利课程设计大作业

数据集介绍 1.背景 根据《中国科学&#xff1a;信息科学》期刊上的一篇文章&#xff0c;量子通信包括多种协议与应用类型&#xff1a; 基于量子隐形传态与量子存储中继等技术&#xff0c;可实现量子态信息传输&#xff0c;进而构建量子信息网络&#xff0c;已成为当前科研热点&…

HTTP参数类型中的Query和Body参数

在接口中常见到query参数和body参数&#xff0c;那么它对应的传参方式是&#xff1f; ★ query查询参数 ---> params ---> route.params.参数 ★ body请求体参数 ---> data ---> route.query.参数 总结&#xff1a; GET请求只能传Query参数&#xff0c; POST请…

网络编程day05(IO多路复用)

今日任务&#xff1a; TCP多路复用的客户端、服务端&#xff1a; 服务端代码&#xff1a; #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <unistd.h> …

【动态规划刷题 17】回文子串 最长回文子串

647. 回文子串 链接: 647. 回文子串 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 具有不同开始位置或结束位置的子串&#xff0c;即使是由…

2023华为杯研究生数学建模F题思路分析

更多思路代码查看文末名片 1.如何有效应用双偏振变量改进强对流预报&#xff0c;仍是目前气象预报的重点难点问题。请利用题目提供的数据&#xff0c;建立可提取用于强对流临近预报双偏振雷达资料中微物理特征信息的数学模型。临近预报的输入为前面一小时&#xff08;10帧&…

不再跳票Fedora 26 正式发布!

经过延期和跳票&#xff0c;Fedora 26终于和大家见面了&#xff0c;下面是Fedora 项目负责人Matthew Miller感谢信 大家好&#xff0c;我很高兴地宣布&#xff0c;从即刻起 Fedora 26 正式可用了。你可以从下面了解到具体信息&#xff0c;也可以马上开始下载&#xff1a; •下载…

CentOS在应用程序菜单中创建快捷方式

背景&#xff1a; 在CentOS系统中&#xff0c;安装一些应用软件的时候&#xff0c;我们可能会自定义安装路径&#xff1b;这样在安装完应用程序后&#xff0c;在“Application”下&#xff0c;可能找不到对应的快捷键&#xff1b;这是就需要手动去创建跨界方式。 应用&#xf…

stm32之GPIO库函数点灯分析

stm32官方为了方便开发者&#xff0c;利用CubeMX 生成HAL库有关的C代码。HAL库就是硬件抽象层(hardware abstraction layer)&#xff0c;生成一系列的函数帮助我们快速生成工程&#xff0c;脱离复杂的寄存器配置。stm32相对于51来功能强大&#xff0c;但是寄存器的数量也不是一…

MySQL备份及恢复

目录 MySQL备份 MySQL备份方法 备份策略 mysql的完全备份 mysql的增量备份 MySQL恢复 mysql完全恢复 mysql增量备份的恢复 MySQL备份 MySQL备份是基于对MySQL的日志进行备份&#xff0c;且恢复也是通过日志进行数据恢复。 MySQL备份方法 物理备份&#xff1a;直接对…

北京智和信通亮相2023IT运维大会,共话数智浪潮下自动化运维新生态

2023年9月21日&#xff0c;由IT运维网、《网络安全和信息化》杂志社联合主办的“2023&#xff08;第十四届&#xff09;IT运维大会”在北京成功举办。大会以“以数为基 智引未来”为主题&#xff0c;北京智和信通技术有限公司&#xff08;下文简称&#xff1a;北京智和信通&…

爱看小说手机网源码全站带数据带自动采集程序/ThinkPHP内核小说网站源码+书库数据库带自动采集

爱看小说手机网源码全站带数据带自动采集程序&#xff0c;爱看小说程序源码2W条数据全站打包,自动采集程序网站源码,后台已经更新5个采集规则可以采集小说30万本大概约10G。 分享的这一款自带2w数据爱看小说网源码全站带数据打包,ThinkPHP内核小说网站源码带听书等全部插件&am…

TouchGFX之画布控件

TouchGFX的画布控件&#xff0c;在使用相对较小的存储空间的同时保持高性能&#xff0c;可提供平滑、抗锯齿效果良好的几何图形绘制。 TouchGFX 设计器中可用的画布控件&#xff1a; LineCircleShapeLine Progress圆形进度条 存储空间分配和使用​ 为了生成反锯齿效果良好的…

矩阵论—凯莱-哈密顿定理

凯莱-哈密顿定理内容 凯莱-哈密顿定理典型例题 典型例题 我们先来观察这个题目&#xff0c;题目要求&#xff0c;若直接将矩阵A 代入计算&#xff0c;则会非常复杂&#xff0c;因此&#xff0c;这条路是走不通的。 我们试着引入我们今天介绍的凯莱-哈密顿定理来解这个…

Linux,计算机网络,数据库

Linux&#xff0c;计算机网络&#xff0c;数据库&#xff0c;操作系统 一、Linux1、linux查看进程2、linux基本命令3、top命令、查看磁盘 二、计算机网络1、HTTP的报文段请求 Repuest响应 Response 2、HTTP用的什么连接3、TCP的三次握手与四次挥手三次握手四次挥手 4、在浏览器…

【MATLAB第76期】基于MATLAB的代表性样本筛选方法合集(针对多输入单输出数据)

【MATLAB第76期】基于MATLAB的代表性样本筛选方法合集&#xff08;针对多输入单输出数据&#xff09; 前有筛选变量方法&#xff0c;如局部敏感性分析和全局敏感性分析方法介绍 。 今天提出另外一种思路&#xff0c;去对样本进行筛选。 使用场景&#xff1a; 场景1&#xff1a…