嵌入式,現(xiàn)在把我的程序和設(shè)計(jì)思路分享給大家。
軟件所用版本如下
Proteus版本 SP 8.7
STM32CubeMX 版本 5.4.0
HAL固件庫版本 1.8.0
Keil 版本 uVison5
一、設(shè)計(jì)思路: 使用STM32CubeMX軟件進(jìn)行資源初始化(Cube資源配置很方便),使用HAL庫進(jìn)行程序設(shè)計(jì)。 圖1 資源配置圖 二、系統(tǒng)功能介紹: - 可調(diào)轉(zhuǎn)速
- 可控轉(zhuǎn)動方向
- 顯示轉(zhuǎn)速和目標(biāo)轉(zhuǎn)速
三、軟件設(shè)計(jì)說明: - 可調(diào)轉(zhuǎn)速通過可調(diào)電阻和STM32的ADC功能,實(shí)現(xiàn)500-4596范圍的速度調(diào)節(jié)。使用的是簡單的比例控制,并未使用復(fù)雜的PID控制(太難了,一直調(diào)不好)。
- 通過定時(shí)器1的PWM互補(bǔ)輸出六路PWM控制電機(jī)的轉(zhuǎn)動,驅(qū)動器使用L293D和IRF540 MOS管。
- 換向使用的是外部中斷,測速使用的是定時(shí)器2的三鹿輸入捕獲,這里有一個(gè)坑,proteus中三路輸入捕獲無法同時(shí)工作,本來打算三路都做測速邏輯,但是速度變化很大,所以最后只使用了一路作為測速通道。
- 正反轉(zhuǎn)使用的是外部中斷。
- 顯示轉(zhuǎn)速和目標(biāo)轉(zhuǎn)速使用的是lcd1602,在proteus仿真中,顯示轉(zhuǎn)速有一定的延時(shí)和誤差(其實(shí)是proteus的仿真太慢了,多開一個(gè)任務(wù)就慢的要死)
6、使用ucosii進(jìn)入分功能多任務(wù)處理。
四、調(diào)試及運(yùn)行結(jié)果 圖2 電機(jī)剛啟動,速度未達(dá)到最小速度500
速度未達(dá)到最小速度時(shí)加載很快,大概加載到300rpm左右開始pid控制。
圖3 仿真過程中 圖4 仿真過程中
圖5 反轉(zhuǎn)時(shí)的調(diào)速過程
仿真過程中可以看到定時(shí)器PWM輸出之間的切換以及脈寬的變化。 圖6 接近穩(wěn)定時(shí)
圖7 反轉(zhuǎn)時(shí)接近穩(wěn)定
圖8 穩(wěn)定后增大轉(zhuǎn)速
由于proteus中stm32 的定時(shí)器計(jì)時(shí)很坑,延時(shí)根本不對,需要修改芯片的時(shí)鐘源頻率,改大了仿真慢,改小了又不準(zhǔn),所以ADC采樣值和轉(zhuǎn)速之間只能近似轉(zhuǎn)換,也造成了目標(biāo)轉(zhuǎn)速和實(shí)際轉(zhuǎn)速的誤差。 圖9 整體電路圖 五、心得體會 在這次設(shè)計(jì)過程中,期間遇到許許多多問題,對電機(jī)的控制不熟悉導(dǎo)致?lián)Q向失敗,仿真過程不收斂,定時(shí)器不起作用,引腳之間相互干擾,輸入捕獲無法同時(shí)進(jìn)行,pwm模式設(shè)置錯(cuò)誤導(dǎo)致pid控制越調(diào)速度越快等等問題,最后都比較好的解決了這些問題,當(dāng)然程序和設(shè)計(jì)中還存在一些問題,由于時(shí)間關(guān)系無法全部解決,在以后的學(xué)習(xí)過程中,如果有機(jī)會會繼續(xù)深入學(xué)習(xí)。
單片機(jī)源程序如下:
- /* USER CODE END Header */
- /* Includes ------------------------------------------------------------------*/
- #include "main.h"
- #include "adc.h"
- #include "tim.h"
- #include "gpio.h"
- /* Private includes ----------------------------------------------------------*/
- /* USER CODE BEGIN Includes */
- #include "includes.h"
- #include "lcd.h"
- /* USER CODE END Includes */
- /* Private typedef -----------------------------------------------------------*/
- /* USER CODE BEGIN PTD */
- /* USER CODE END PTD */
- /* Private define ------------------------------------------------------------*/
- /* USER CODE BEGIN PD */
- #define HALL_GPIO GPIOA
- //START 任務(wù)
- //設(shè)置任務(wù)優(yōu)先級
- #define START_TASK_PRIO 10 //開始任務(wù)的優(yōu)先級設(shè)置為最低
- //設(shè)置任務(wù)堆棧大小
- #define START_STK_SIZE 64
- //任務(wù)堆棧
- OS_STK START_TASK_STK[START_STK_SIZE];
- //任務(wù)函數(shù)
- void start_task(void *pdata);
-
- //LED0任務(wù)
- //設(shè)置任務(wù)優(yōu)先級
- #define LED0_TASK_PRIO 2
- //設(shè)置任務(wù)堆棧大小
- #define LED0_STK_SIZE 64
- //任務(wù)堆棧
- OS_STK LED0_TASK_STK[LED0_STK_SIZE];
- //任務(wù)函數(shù)
- void led0_task(void *pdata);
- //Speed_ADC 任務(wù)
- //設(shè)置任務(wù)優(yōu)先級
- #define SPEED_ADC_TASK_PRIO 1
- //設(shè)置任務(wù)堆棧大小
- #define SPEED_ADC_STK_SIZE 64
- //任務(wù)堆棧
- OS_STK SPEED_ADC_TASK_STK[SPEED_ADC_STK_SIZE];
- //任務(wù)函數(shù)
- void speed_adc_task(void *pdata);
- /* USER CODE END PD */
- /* Private macro -------------------------------------------------------------*/
- /* USER CODE BEGIN PM */
- /* USER CODE END PM */
- /* Private variables ---------------------------------------------------------*/
- /* USER CODE BEGIN PV */
- //定時(shí)器2捕獲通道參數(shù)
- /* Private variables ---------------------------------------------------------*/
- uint16_t Channel1HighTime, Channel2HighTime, Channel3HighTime; //高電平時(shí)間
- uint16_t Channel1Period, Channel2Period, Channel3Period; //周期
- uint8_t Channel1Edge = 0, Channel2Edge = 0, Channel3Edge = 0; //上升沿
- uint16_t Channel1Percent, Channel2Percent, Channel3Percent; //占空比
- uint16_t Channel1PercentTemp[3] = {0, 0, 0};
- uint8_t Channel1TempCount = 0;
- uint16_t Channel1RisingTimeLast=0, Channel1RisingTimeNow, Channel1FallingTime;
- uint16_t Channel2RisingTimeLast=0, Channel2RisingTimeNow, Channel2FallingTime;
- uint16_t Channel3RisingTimeLast=0, Channel3RisingTimeNow, Channel3FallingTime;
- extern int motor_period;
- extern int motor_duty;
- extern int clock_wise;
- int current_speed = 0;
- int ADC_Speed = 500; //555 / 90% = 500
- int ADC_Value = 555; //
- BOOLEAN state = 0; // 0 關(guān)閉中 1 啟動中
- /* USER CODE END PV */
- /* Private function prototypes -----------------------------------------------*/
- void SystemClock_Config(void);
- /* USER CODE BEGIN PFP */
- /* USER CODE END PFP */
- /* Private user code ---------------------------------------------------------*/
- /* USER CODE BEGIN 0 */
- /* USER CODE END 0 */
- /**
- * @brief The application entry point.
- * @retval int
- */
- int main(void)
- {
- /* USER CODE BEGIN 1 */
- HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);//設(shè)置中斷優(yōu)先級分組為組2:2位搶占優(yōu)先級,2位響應(yīng)優(yōu)先級
- OSInit();
- OSTaskCreate(start_task,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );//創(chuàng)建起始任務(wù)
- /* USER CODE END 1 */
-
- /* MCU Configuration--------------------------------------------------------*/
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
- HAL_Init();
- /* 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();
- MX_ADC1_Init();
- MX_TIM2_Init();
- /* USER CODE BEGIN 2 */
- OSStart();
- /* USER CODE END 2 */
- /* Infinite loop */
- /* USER CODE BEGIN WHILE */
- while (1)
- {
- /* USER CODE END WHILE */
- /* USER CODE BEGIN 3 */
- }
- /* USER CODE END 3 */
- }
- /**
- * @brief System Clock Configuration
- * @retval None
- */
- void SystemClock_Config(void)
- {
- RCC_OscInitTypeDef RCC_OscInitStruct = {0};
- RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
- RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
- /** Initializes the CPU, AHB and APB busses clocks
- */
- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
- RCC_OscInitStruct.HSEState = RCC_HSE_ON;
- RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
- RCC_OscInitStruct.HSIState = RCC_HSI_ON;
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
- RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
- RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL2;
- if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
- {
- Error_Handler();
- }
- /** Initializes the CPU, AHB and APB busses clocks
- */
- RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
- |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
- RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
- RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV2;
- RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
- RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
- if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
- {
- Error_Handler();
- }
- PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
- PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV2;
- if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
- {
- Error_Handler();
- }
- }
- /* USER CODE BEGIN 4 */
- //開始任務(wù)
- void start_task(void *pdata)
- {
- // //設(shè)置通道1的脈寬。 width = (1000 - 500) / 1000 = 50%
- __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, motor_duty);
- __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, motor_duty);
- __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, motor_duty);
-
- //打開定時(shí)器2通道 , 中斷使能
- HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
- HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2);
- HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_3);
- HAL_Delay(100);
- //開啟定時(shí)器1的通道1
- HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);
- HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2);
- HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_3);
- //
- HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
- HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
- HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
-
- //
- uint16_t hall_read = (HALL_GPIO->IDR)&0x0007; // 獲取霍爾傳感器狀態(tài) pin0 1 2__IO uint8_t uwStep = 0;
- BLDC_PHASE_CHANGE(hall_read); // 驅(qū)動換相
-
- //PID初始化
- Speed_PIDInit();
-
- OS_CPU_SR cpu_sr=0;
- OS_ENTER_CRITICAL(); //進(jìn)入臨界區(qū)(無法被中斷打斷)
- OSTaskCreate(led0_task,(void *)0,(OS_STK*)&LED0_TASK_STK[LED0_STK_SIZE-1],LED0_TASK_PRIO);
- OSTaskCreate(speed_adc_task,(void *)0,(OS_STK*)&SPEED_ADC_TASK_STK[SPEED_ADC_STK_SIZE-1],SPEED_ADC_TASK_PRIO);
- OSTaskSuspend(START_TASK_PRIO); //掛起起始任務(wù).
- OS_EXIT_CRITICAL(); //退出臨界區(qū)(可以被中斷打斷)
- }
- //LED0任務(wù)
- void speed_adc_task(void *pdata)
- {
- lcd_system_reset();
- unsigned char temp_table[16] ={"Cur_Speed:"};
- unsigned char temp_table1[16] ={"Tar_Speed:"};
- for(uint8_t i=0;i<10;i++)
- {
- lcd_char_write(i,0,temp_table[i]);
- lcd_char_write(i,1,temp_table1[i]);
- }
- HAL_ADC_Start(&hadc1);
- while(1)
- {
- HAL_ADC_PollForConversion(&hadc1,0); //等待轉(zhuǎn)換完成,第二個(gè)參數(shù)代表最長等待時(shí)間ms
- if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
- {
- ADC_Value = HAL_ADC_GetValue(&hadc1); // 讀取ADC數(shù)據(jù) ,4096 -> 3.3V
- ADC_Speed = ADC_Value + 500; //轉(zhuǎn)換公式 0-4096 -> 500 - 4596
- // if(ADC_Speed > 100){
- // HAL_GPIO_TogglePin(led_GPIO_Port, led_Pin);
- // }
- }
- //當(dāng)前速度
- temp_table[10]=current_speed/1000+'0';
- temp_table[11]=current_speed/100%10+'0';
- temp_table[12]=current_speed/10%10+'0';
- temp_table[13]=current_speed%10+'0';
- //目標(biāo)速度
- temp_table1[10]=ADC_Speed/1000+'0';
- temp_table1[11]=ADC_Speed/100%10+'0';
- temp_table1[12]=ADC_Speed/10%10+'0';
- temp_table1[13]=ADC_Speed%10+'0';
- for(uint8_t i=10;i<14;i++)
- {
- lcd_char_write(i,0,temp_table[i]);
- lcd_char_write(i,1,temp_table1[i]);
- }
- }
- }
- //speed adc 采樣函數(shù)
- void led0_task(void *pdata)
- {
- while(1)
- {
- HAL_GPIO_WritePin(led_GPIO_Port, led_Pin, GPIO_PIN_SET);
- OSTimeDly(10);
- HAL_GPIO_WritePin(led_GPIO_Port, led_Pin, GPIO_PIN_RESET);
- OSTimeDly(10);
- }
- }
- //外部中斷服務(wù)函數(shù)
- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
- {
- if(!state)
- {
- __IO uint8_t uwStep = 0;
- uint16_t hall_read=(HALL_GPIO->IDR)&0x0007; // 獲取霍爾傳感器狀態(tài) pin0 1 2
- uwStep = hall_read;
- BLDC_PHASE_CHANGE(uwStep); // 驅(qū)動換相
-
- }
- uint16_t key_read =(Start_GPIO_Port->IDR)&0x00e0;
- if(key_read == 0x00c0)
- {
- // state = !state;
- // HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);
- // HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_2);
- // HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_3);
- //
- // //BLDC_PHASE_CHANGE(7);
- // HAL_TIM_Base_MspDeInit(&htim1);
- //
- // HAL_Delay(300);
- // HAL_TIM_Base_MspDeInit(&htim1);
- // HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
- // HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
- // HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
- // BLDC_PHASE_CHANGE(7);
- //HAL_GPIO_TogglePin(led_GPIO_Port, led_Pin);
- }else if(key_read == 0x00a0)
- {
- clock_wise = 0;
- }else if(key_read == 0x0060)
- {
- clock_wise = 1;
- }
- }
- //定時(shí)器2中斷函數(shù)
- //溢出時(shí)間為1s
- //溢出值1000 每個(gè)點(diǎn)為1ms
- void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
- {
-
- if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) //捕獲中斷
- {
- /*
- 測速邏輯
- 1、中斷產(chǎn)生,先判斷是否為第一次上升沿
- 2、捕獲到上升沿后,將時(shí)間點(diǎn)存入變量,切換捕獲下降沿
- 3、捕獲到下降沿后,記下時(shí)間點(diǎn),切換為捕獲上升沿
- 4、捕獲到上升沿后,記下時(shí)間點(diǎn)
- 5、計(jì)算周期和占空比
- 6、問題如果經(jīng)過多個(gè)周期才有一次上升沿和下降沿怎么辦,需要記錄溢出次數(shù)
- 如果溢出的時(shí)候有上升沿標(biāo)志位
-
- 問題:proteus三路輸入捕獲計(jì)算,測轉(zhuǎn)速時(shí),如果第一個(gè)上升沿和第二個(gè)上升沿不在一個(gè)定時(shí)器計(jì)數(shù)周期,會計(jì)算失敗
- */
- if(Channel1Edge == 0)
- {
- //獲取通道1上升沿時(shí)間點(diǎn)
- Channel1RisingTimeNow = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1);
- Channel1Edge = 1;//捕獲上升沿置位
- Channel1RisingTimeLast = Channel1RisingTimeNow;
- }else if(Channel1Edge == 1)
- {
- Channel1RisingTimeNow = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1);
- if(Channel1RisingTimeNow > Channel1RisingTimeLast)
- {
- Channel1Period = Channel1RisingTimeNow - Channel1RisingTimeLast;
- }
- else
- {
- //Channel2Period = Channel2RisingTimeNow + 1000 - Channel2RisingTimeLast + 1;
- }
- Channel1Edge = 0;
- //pid計(jì)算
- // current_speed = 60*1000 / Channel1Period; //轉(zhuǎn)速計(jì)算
- // current_speed = current_speed * 5; //速度調(diào)整系數(shù)
- // motor_duty = Speed_PIDAdjust(current_speed);
- }
- }else if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
- {
- if(Channel2Edge == 0)
- {
- Channel2RisingTimeNow = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_2);
- Channel2Edge = 1;
-
- Channel2RisingTimeLast = Channel2RisingTimeNow;
- }
- else if(Channel2Edge == 1)
- {
- Channel2RisingTimeNow = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_2);
- if(Channel2RisingTimeNow > Channel2RisingTimeLast)
- {
- Channel2Period = Channel2RisingTimeNow - Channel2RisingTimeLast;
- }
- else
- {
- //Channel2Period = Channel2RisingTimeNow + 1000 - Channel2RisingTimeLast + 1;
- }
- current_speed = 60*1000 / Channel2Period;
- current_speed = current_speed * 5; //速度調(diào)整系數(shù)
- motor_duty = Speed_PIDAdjust(current_speed);
- Channel2Edge = 0;
- }
- }
- else if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_3)
- {
- if(Channel3Edge == 0)
- {
- Channel3RisingTimeNow = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_3);
- Channel3Edge = 1;
- Channel3RisingTimeLast = Channel3RisingTimeNow;
- }
- else if(Channel3Edge == 1)
- {
- Channel3RisingTimeNow = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_3);
- if(Channel3RisingTimeNow > Channel3RisingTimeLast)
- {
- Channel3Period = Channel3RisingTimeNow - Channel3RisingTimeLast;
- }
- else
- {
- //Channel3Period = Channel3RisingTimeNow + 1000 - Channel3RisingTimeLast + 1;
- }
- // current_speed = 60*1000 / Channel3Period;
- // current_speed = current_speed * 5; //速度調(diào)整系數(shù)
- // motor_duty = Speed_PIDAdjust(current_speed);
- Channel3Edge = 0;
- }
- }
- }
- /* USER CODE END 4 */
- /**
- * @brief This function is executed in case of error occurrence.
- * @retval None
- */
- void Error_Handler(void)
- {
- /* USER CODE BEGIN Error_Handler_Debug */
- /* User can add his own implementation to report the HAL error return state */
- /* USER CODE END Error_Handler_Debug */
- }
- #ifdef USE_FULL_ASSERT
- /**
- * @brief Reports the name of the source file and the source line number
- * where the assert_param error has occurred.
- * @param file: pointer to the source file name
- * @param line: assert_param error line source number
- * @retval None
- */
- void assert_failed(uint8_t *file, uint32_t line)
- {
- /* USER CODE BEGIN 6 */
- /* User can add his own implementation to report the file name and line number,
- tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
- /* USER CODE END 6 */
- }
- #endif /* USE_FULL_ASSERT */
- /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
復(fù)制代碼
所有資料51hei提供下載:
Proteus.zip
(102.55 KB, 下載次數(shù): 554)
2020-1-21 09:26 上傳
點(diǎn)擊文件名下載附件
仿真文件
文檔.docx
(758.47 KB, 下載次數(shù): 378)
2020-1-21 16:21 上傳
點(diǎn)擊文件名下載附件
設(shè)計(jì)思路
uCos_ii_Demo.7z
(5.21 MB, 下載次數(shù): 497)
2020-1-21 16:15 上傳
點(diǎn)擊文件名下載附件
STM32程序
|