(1) 掌握基于 PROTEUS 和 KEIL 的仿真调试方法。 (2) 掌握 uCOS-II 的移植,并采用多任务编程、调试。 (3) 熟悉电机的工作原理和功能,并掌握电机的应用和驱动方法。 1.2 内容
(1) 基本功能:本任务通过输出脉冲控制步进电机的停止、运动、方向。使用 两个按键分别控制步进电机的正转和反转,再次按下这两个按键,步进电机停止, 同时 LCD 显示电机状态信息。 (2) 扩展功能:加入一个转速阈值设置功能,由电位器充当阈值设置器,可设 置目标转速并使电机接近设置的转速。
第 2 章 系统硬件设计
2.1 总体框架设计
图 2-1 硬件框架图
本次任务实践的硬件架构设计如图 2-1 所示。按照设计任务的要求,本次设 计采用的主控芯片为 STM32F103,整体硬件设计以该芯片为中心,由按键输入模 块、LCD 显示模块、电机模块等组成。 2.2 硬件电路设计
(1) 参考 LCD1602 液晶显示屏的芯片资料,在 LCD 显示屏的电路连接中,将 VDD 以及 VSS、VEE 接到电源以及地上。之后再将它的3 个信号控制引脚 RS、RW、 E 以及 8 个数据传输引脚 DO-D7 分别连接到 STM32 的相应引脚上。 (2) 按键输入模块采用两个独立按键,分别连接 STM32 的 PB6、PB7 引脚,公 共端接地。 (3) 电机模块使用直流无刷电机,并采用 L293D 和 IRF540 MOS 管驱动。 (4) 用所谓“六步换向法”,根据转子当前的位置,按照一定的顺序给定子绕 组通电使 BLDC 电机转动。如图 2-2 所示。 永磁体 N-S 交替交换,使位置传感器产生相位差 120°的 H3、H2、H1 方波, 从而产生有效的六状态编码信号:010、011、001、101、100、110,通过逻辑组 件处理产生 V6-V1 导通、V5-V6 导通、V4-V5 导通、V3-V4 导通、V2-V3 导通、V1-V2 导通,也就是说将直流母线电压依次加在 U ->V、w ->V、W ->U、V->U、V->W、 U ->W 上,这样转子每转过一对 N-S 极,V1、V2、V3、V4、V5、V6 各功率管即按 固定组合成六种状态的依次导通。
图 2-2 BLDC 控制框图
图 2-3 120°HAll 换相控制图 对于典型的三相带传感器的 BLDC 电机,有 6 个不同的工作区间,每个区间 中有特定的两相绕组通电。通过检测霍尔传感器,可以得到一个 3 位编码,编码 值的范围从 1 到 6。每个编码值代表转子当前所处的区间。从而提供了需要对哪 些绕组通电的信息。因此程序可以使用简单的查表操作来确定要对哪两对特定的 绕组通电以使转子转动。注意状态"0 和 7"对于霍尔效应传感器而言是无效状态。 软件应该检查出这些值并相应地禁止 PWM。 (5) Proteus 的无刷直流电机模型带有 3 个霍尔传感器,霍尔传感器的输出信 号两相间相差 120 度。与此对应的是电机转子每旋转一周霍尔传感器就能输出 6 种编码状态,如图 2-4 所示。 从图可见,霍尔传感器输 出状态变化一次,就 意味着电机转子转过了 60 度。 据此,可以根据单位时间 T 内捕获的霍尔传感 器输出变化的个数 n 计算出电机的转速 V=60n/T。 根据这一原理,通过控制器 的输入捕获功能 IC 获取到其中 一相霍尔传感器输出信号的周期,就可以比较准 精确地测量到电机的转速。
图 2-4 霍尔位置传感器输出信号波形
图 2-5 硬件原理图
第 3 章 系统软件设计

为了方便编程并使代码美观,需要创建一个开始任务。该任务可以用来创建 其他任务以及一些信号量、消息邮箱、事件标志组等。而根据设计要求,需要在 本地 LCD 显示电机运转状态,因此需要一个信息显示任务。该控制系统最终所实 现的功能是控制直流无刷电机,故还需要一个步进电机驱动任务。根据上述分析, 一共划分为两个任务。
图 3-1 主函数流程图 开始任务主要用来对一些驱动电机的寄存器进行初始化并启动电机以及创 建后面所需要的任务以及一些信号量、事件标志组和消息邮箱,任务优先级设置 为 10。
图 3-2 开始任务流程图 速度任务主要将霍尔传感器的数据进行读取并将读到的速度参数进行转换, 之后显示在显示屏上,任务优先级设置为 1。
图 3-3 速度读取任务流程图 速度读取主要是在中断服务函数里进行,通过对捕获霍尔传感器的上升沿以 及下降沿,读的其信号周期,再将其转换为频率得到速度值。
图 3-4 转速读取计算流程图
 第 4 章 调试过程及结果
图 4-1 仿真过程中 图 4-2 反转时的调速过程 仿真过程中可以看到定时器 PWM 输出之间的切换以及脉宽的变化。
图 4-3 接近稳定时
图 4-4 稳定后增大转速
STM32单片机源程序如下:
- 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 任务
- //设置任务优先级
- #define START_TASK_PRIO 10 //开始任务的优先级设置为最低
- //设置任务堆栈大小
- #define START_STK_SIZE 64
- //任务堆栈
- OS_STK START_TASK_STK[START_STK_SIZE];
- //任务函数
- void start_task(void *pdata);
-
- //LED0任务
- //设置任务优先级
- #define LED0_TASK_PRIO 2
- //设置任务堆栈大小
- #define LED0_STK_SIZE 64
- //任务堆栈
- OS_STK LED0_TASK_STK[LED0_STK_SIZE];
- //任务函数
- void led0_task(void *pdata);
- //Speed_ADC 任务
- //设置任务优先级
- #define SPEED_ADC_TASK_PRIO 1
- //设置任务堆栈大小
- #define SPEED_ADC_STK_SIZE 64
- //任务堆栈
- OS_STK SPEED_ADC_TASK_STK[SPEED_ADC_STK_SIZE];
- //任务函数
- 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 */
- //定时器2捕获通道参数
- /* Private variables ---------------------------------------------------------*/
- uint16_t Channel1HighTime, Channel2HighTime, Channel3HighTime; //高电平时间
- 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 关闭中 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);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
- OSInit();
- OSTaskCreate(start_task,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );//创建起始任务
- /* 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 */
- //开始任务
- void start_task(void *pdata)
- {
- // //设置通道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);
-
- //打开定时器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);
- //开启定时器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; // 获取霍尔传感器状态 pin0 1 2__IO uint8_t uwStep = 0;
- BLDC_PHASE_CHANGE(hall_read); // 驱动换相
-
- //PID初始化
- Speed_PIDInit();
-
- OS_CPU_SR cpu_sr=0;
- OS_ENTER_CRITICAL(); //进入临界区(无法被中断打断)
- 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); //挂起起始任务.
- OS_EXIT_CRITICAL(); //退出临界区(可以被中断打断)
- }
- //LED0任务
- 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); //等待转换完成,第二个参数代表最长等待时间ms
- if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
- {
- ADC_Value = HAL_ADC_GetValue(&hadc1); // 读取ADC数据 ,4096 -> 3.3V
- ADC_Speed = ADC_Value + 500; //转换公式 0-4096 -> 500 - 4596
- // if(ADC_Speed > 100){
- // HAL_GPIO_TogglePin(led_GPIO_Port, led_Pin);
- // }
- }
- //当前速度
- 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';
- //目标速度
- 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 采样函数
- 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);
- }
- }
- //外部中断服务函数
- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
- {
- if(!state)
- {
- __IO uint8_t uwStep = 0;
- uint16_t hall_read=(HALL_GPIO->IDR)&0x0007; // 获取霍尔传感器状态 pin0 1 2
- uwStep = hall_read;
- BLDC_PHASE_CHANGE(uwStep); // 驱动换相
-
- }
- 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;
- }
- }
- //定时器2中断函数
- //溢出时间为1s
- //溢出值1000 每个点为1ms
- void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
- {
-
- if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) //捕获中断
- {
- /*
- 测速逻辑
- 1、中断产生,先判断是否为第一次上升沿
- 2、捕获到上升沿后,将时间点存入变量,切换捕获下降沿
- 3、捕获到下降沿后,记下时间点,切换为捕获上升沿
- 4、捕获到上升沿后,记下时间点
- 5、计算周期和占空比
- 6、问题如果经过多个周期才有一次上升沿和下降沿怎么办,需要记录溢出次数
- 如果溢出的时候有上升沿标志位
-
- 问题:proteus三路输入捕获计算,测转速时,如果第一个上升沿和第二个上升沿不在一个定时器计数周期,会计算失败
- */
- if(Channel1Edge == 0)
- {
- //获取通道1上升沿时间点
- 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计算
- // current_speed = 60*1000 / Channel1Period; //转速计算
- // current_speed = current_speed * 5; //速度调整系数
- // motor_duty = Speed_PIDAdjust(current_speed);
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
全部资料51hei下载地址:
代码:
uCos_ii_Demo.7z
(5.21 MB, 下载次数: 297)
|