毕业设计】9-基于STM32无刷直流电机控制器的设计仿真与实现(原理图+源码+仿真工程+论文+PPT+参考英文文献)

毕业设计】9-基于STM32无刷直流电机控制器的设计仿真与实现(原理图+源码+仿真工程+论文+PPT+参考英文文献)

资料要求

包含此题目毕业设计全套资料:
原理图工程文件
原理图截图
仿真模型工程文件
仿真截图
低重复率文档(22642字)
英文文献及翻译
详情请私信!

任务书

1.基于单片机实现无刷直流电机控制器的设计,完成系统芯片选型;
2.确定无刷直流电机控制器的总体设计方案;
3.给出系统的硬件设计,包括主控模块、位置检测模块、PWM驱动模块、换向逻辑模块、逆变模块、速度反馈模块等硬件模块的电路设计;
4.给出系统的软件设计,并绘制主要模块的流程图;
5.基于Proteus对系统进行仿真。

综上分析本设计目标如下:

能够驱动直流无刷电机的运转并有电路保护以免器件烧坏。
能够实时准确的检测到直流无刷电机转子的位置。
能够实现对电机启动和停止的控制。
能够通过滑动变阻器来实现直流无刷电机的无极调速。
电路具有电流、电压保护,以免对电路产生不良影响。
现在已经确定直流无刷无感电机的控制系统需要实现的主要功能和技术有:

能够准确实时的检测到无刷直流电机转子的位置;
用三段式技术使电机能够很好的启动;
PID调节技术;
速度环的控制;
电压保护、电流保护。

设计说明书

摘要

本文的主要工作是基于STM32设计无刷直流电机控制系统。随着科学的进步,电子技术的成熟,现在已经有了很大一部分电子产品开始实现智能化,并且已经开始广泛的应用于当前的生活中来,通过嵌入式设备来使系统达到更好的技术的控制。本文选择使用STM32主控芯片控制无刷直流电机,可以通过按键实现对无刷直流电机的速度控制,并可以将转速显示到液晶显示器。
本文首先阐述了无刷直流电机的研究背景和意义,然后对无刷直流电机实现方案进行了论证分析,并给出无刷直流电机的总体设计方案。接着详细介绍了无刷直流电机的硬件设计,对主控模块,电源模块,显示模块的电路原理图进行了绘制,重点是无刷直流电机的驱动模块和调速模块的程序设计。最后基于Proteus对系统进行仿真、调试。通过这种方式,不仅能精确验证设计后的系统是否满足技术要求,还在提高系统效率、质量的设计基础上降低了开发成本。

设计框架架构

电机调速框架思路:
在这里插入图片描述

设计说明书及设计文件

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

低重复率文档(22642字)在这里插入图片描述

源码展示

/* USER CODE BEGIN Header */
/**  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *  ******************************************************************************
  */
/* 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 任务
//设置任务优先级
#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 */
/**
  * @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();
  }
}
//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);
		}
	}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; //速度调整系数
			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; //速度调整系数
//			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****/
#define ADC_Speed_Max   4596	 //800 3500
#define ADC_Speed_Min   500
#define PWM_Max			    800
#define PWM_Min     	  60
extern int ADC_Speed;  //ADC采样值转换成转速  
extern int motor_period;
extern int motor_duty;   
/*==================================================================================================== 
                                                增量式PID
The PID (????????) function is used in mainly 
control applications. PIDCalc performs one iteration of the PID 
algorithm. 
While the PID function works, main is just a dummy program showing 
a typical usage. 
=====================================================================================================*/
typedef struct PID 
{ 
        int Target;     					//目标转速  相差10%左右,所以显示90%
        int Uk;                 	//Uk
        int Udk;                	//Udk
        int Uk_1;                 //Uk-1
        double P;                 // Proportional Const 
        double I;                 // Integral Const
        int    b; 

}PID;
static PID Speed_PID; 
static PID *Speed_Point = &Speed_PID; 
/*==================================================================================================== 
Initialize PID Structure PID
=====================================================================================================*/ 
void Speed_PIDInit(void) 
{ 
        Speed_Point->Target = ADC_Speed *10 / 9;
        Speed_Point->Uk     = 0;
        Speed_Point->Udk    = 0;
        Speed_Point->Uk_1   = PWM_Min;
        Speed_Point->ek_0   = 0;        					 //ek=0
        Speed_Point->ek_1   = 0;        					 //ek-1=0
        Speed_Point->ek_2   = 0;         					 //ek-2=0
        Speed_Point->P      = 1;  					  //Proportional Const 

}
/*==================================================================================================== 
???PID???? 
=====================================================================================================*/ 
int Speed_PIDAdjust(int Next_Point) 
{ 
				Speed_Point->Target = ADC_Speed *10 / 9;  //重新调整速度
        Speed_Point->ek_0= Speed_Point->Target - Next_Point;         
        if(((Speed_Point->Uk_1>=PWM_Max)&&(Speed_Point->ek_0>=0))||((Speed_Point->Uk_1<=PWM_Min)&&(Speed_Point->ek_0<=0)))
        {
            Speed_Point->b=0;
        } 
        else
        {
            Speed_Point->b=1;
        } 
        //Speed_Point->Udk=Speed_Point->P*(Speed_Point->ek_0-Speed_Point->ek_1) + Speed_Point->b*Speed_Point->I*Speed_Point->ek_0 
          //      + Speed_Point->D*(Speed_Point->ek_0-2*Speed_Point->ek_1+Speed_Point->ek_2);//PID
				Speed_Point->Udk=Speed_Point->P*(Speed_Point->ek_0-Speed_Point->ek_1);//P
        Speed_Point->Uk = Speed_Point->Uk_1 + Speed_Point->Udk;
        Speed_Point->ek_2 = Speed_Point->ek_1;
        Speed_Point->ek_1 = Speed_Point->ek_0;
        Speed_Point->Uk_1 = Speed_Point->Uk; 
    if(Speed_Point->Uk >= PWM_Max)
        {
          return PWM_Max;
        }
        else if(Speed_Point->Uk <= PWM_Min)
        {
					return PWM_Min;
        } 
        return(Speed_Point->Uk); 
}

  • 82
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cqtianxingkeji

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值