關於STM32CubeMX使用LL庫設置PWM輸出


HAL和LL庫

  HAL是ST為了實現代碼在ST家族的MCU上的移植性,推出的一個庫,稱為硬件抽象層,很明顯,這樣做將會犧牲存儲資源,所以項目最后的代碼比較冗余,且運行效率大大降低,運行速度受制於flash的速度,所以很多人設計的時候出現了各種各樣的問題。而LL庫更精簡,他更接近底層,直接操作寄存器來實現,二者在資源消耗上別人已做過比較,https://blog.csdn.net/wping1234/article/details/80197287。個人更看重代碼的效率以及精簡,所以選擇使用LL庫。

利用LL配置TIM1輸出PWM

  1. 首先進行時鍾配置,手中STM32F1的板子外部晶振為8MHz,將系統的主頻配置為72MHz,得益於STM32CubeMX的可視化配置設計,時鍾的配置變得輕松起來
  2. 使用STM32CubeMX配置定時器TIM1,本次設計PWM的周期為1s,將PWM輸出控制LED燈,可以看出明顯的效果,因此將TIM1的時鍾進行7200的分頻,對其計數10000次將會得到1s的定時時間,配置如下:

      

 

 為了可以調節頻率,保證調整后的頻率可以維持1個周期,開啟update中斷。也可不開啟,可輸出PWM。

選擇LL庫生成代碼:

 

  

其他的SWD的配置省略,配置好后使用STM32CubeMX生成代碼。

   軟件對LL庫的支持不及HAL,生成后需要手動修改和添加少量代碼。

這兩個地方是我手動修改的,自動生成的這一部分存在錯誤,請注意。

要開啟定時器,則要使用LL庫的庫函數,關於定時器的控制的庫函數在以下這個文件,

我們可以打開這個文件,然后打開他的頭文件查看里面可用的函數,使用MDK查看不太方便,可以使用notepad++查看,利用notepad++的函數列表很方便,關於TIM的定時器,打開它需要尋找ENABLE這個關鍵字:

有些人說LL庫沒有HAL好理解,需要看手冊,其實不需要,看這個庫文件就可以了,然后對單片機有一定的了解就可以使用了。這里我們肯定需要使能計數器,.h文件中直接看函數就能知道函數功能

/** @defgroup TIM_LL_EF_Time_Base Time Base configuration * @{ */
/** * @brief Enable timer counter. * @rmtoll CR1 CEN LL_TIM_EnableCounter * @param TIMx Timer instance * @retval None */ __STATIC_INLINE void LL_TIM_EnableCounter(TIM_TypeDef *TIMx) { SET_BIT(TIMx->CR1, TIM_CR1_CEN); }

而我們配置的定時器RIM1的ch1,所以看看是否有chi的使能函數,LL庫很有規律,關於通道配置的都有CC這兩個字母,包括配ch1

 

我們只是用了TIM1的ch1,就要使能ch1,只需要搜索LL_TIM_CC_E

/** * @brief Enable capture/compare channels. * @rmtoll CCER CC1E LL_TIM_CC_EnableChannel\n * CCER CC1NE LL_TIM_CC_EnableChannel\n * CCER CC2E LL_TIM_CC_EnableChannel\n * CCER CC2NE LL_TIM_CC_EnableChannel\n * CCER CC3E LL_TIM_CC_EnableChannel\n * CCER CC3NE LL_TIM_CC_EnableChannel\n * CCER CC4E LL_TIM_CC_EnableChannel * @param TIMx Timer instance * @param Channels This parameter can be a combination of the following values: * @arg @ref LL_TIM_CHANNEL_CH1 * @arg @ref LL_TIM_CHANNEL_CH1N * @arg @ref LL_TIM_CHANNEL_CH2 * @arg @ref LL_TIM_CHANNEL_CH2N * @arg @ref LL_TIM_CHANNEL_CH3 * @arg @ref LL_TIM_CHANNEL_CH3N * @arg @ref LL_TIM_CHANNEL_CH4 * @retval None */ __STATIC_INLINE void LL_TIM_CC_EnableChannel(TIM_TypeDef *TIMx, uint32_t Channels) { SET_BIT(TIMx->CCER, Channels); }

將這兩個函數加入到初始化后,發現還是不能運行定時器,再進頭文件看看,是不是還需要使能什么,搜索LL_TIM_E

這有個輸出使能,看看函數功能:

/** * @brief Enable the outputs (set the MOE bit in TIMx_BDTR register). * @note The MOE bit in TIMx_BDTR register allows to enable /disable the outputs by * software and is reset in case of break or break2 event * @note Macro @ref IS_TIM_BREAK_INSTANCE(TIMx) can be used to check whether or not * a timer instance provides a break input. * @rmtoll BDTR MOE LL_TIM_EnableAllOutputs * @param TIMx Timer instance * @retval None */ __STATIC_INLINE void LL_TIM_EnableAllOutputs(TIM_TypeDef *TIMx) { SET_BIT(TIMx->BDTR, TIM_BDTR_MOE); }

可以發現是使能輸出,加上這三個函數后定時器成功工作,LED以1Hz的頻率閃爍,成功了!!

int main(void) { /* USER CODE BEGIN 1 */ uint32_t duty = 0; /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_AFIO); LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR); NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); /* System interrupt init*/

  /* Peripheral interrupt init*/
  /* RCC_IRQn interrupt configuration */ NVIC_SetPriority(RCC_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0)); NVIC_EnableIRQ(RCC_IRQn); /** NOJTAG: JTAG-DP Disabled and SW-DP Enabled */ LL_GPIO_AF_Remap_SWJ_NOJTAG(); /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM1_Init(); /* Initialize interrupts */ MX_NVIC_Init(); /* USER CODE BEGIN 2 */ LL_TIM_CC_EnableChannel(TIM1,LL_TIM_CHANNEL_CH1); LL_TIM_EnableCounter(TIM1); LL_TIM_EnableAllOutputs(TIM1); //HAL_TIM_Base_Start(&htim1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1) { LL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin); delay_ms(500); // HAL_Delay(10);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }

如果需要設置占空比,前面已經提到,關於每個輸出通道的設置都有CC,再一想是要設置,那么應該是有Set,於是搜索LL_TIM_CC_S

 

哎呀,打臉沒有找到,那么想一想,前面是配置通道的有CC,那應該是有一個C才對,發現里面有OC的關鍵字,搜索LL_TIM_OC_S試試

嗯哼,找到了,看看他的定義:

/** * @brief Set compare value for output channel 1 (TIMx_CCR1). * @note Macro @ref IS_TIM_CC1_INSTANCE(TIMx) can be used to check whether or not * output channel 1 is supported by a timer instance. * @rmtoll CCR1 CCR1 LL_TIM_OC_SetCompareCH1 * @param TIMx Timer instance * @param CompareValue between Min_Data=0 and Max_Data=65535 * @retval None */ __STATIC_INLINE void LL_TIM_OC_SetCompareCH1(TIM_TypeDef *TIMx, uint32_t CompareValue) { WRITE_REG(TIMx->CCR1, CompareValue); }

其實在配置定時器的時候也能發現一些端倪。

前面也提到中斷,經過前面的配置,我依稀覺得LL庫自動生成的都是給我們配置好最基本的,我們要使用需要打開相應的開關,使能對應的選項。我想中斷也應該是一樣,到函數列表看一看:

嗯哼,還真有,打開這個開關,開啟調試模式后驗證,果不其然,要想使用中斷,就需要使能這個中斷。

總結

LL庫的使用相較於HAL來說,似乎更加的晦澀,但是當你大概了解了這個LL庫的設計框架,加上對單片機有一定的了解,根據庫函數的函數列表就能成功的使用LL庫。

祝大家學習順利!

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM