基于stm32的减速直流电机PID算法控制

这篇具有很好参考价值的文章主要介绍了基于stm32的减速直流电机PID算法控制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本例程采用了HAL库进行项目开发(主要使用软件CubexMX和keil5),文章末尾会有代码开源,欢迎各位对文章进行指正和探讨。

基于PID的减速电机控制

一、 硬件模块与原理图   

1、硬件组成   

        硬件组成:stm32f103c8t6最小系统板;0.96寸LED12864(I2C通讯模式);智能小车12v移动电源;25GA370直流减速电机(带霍尔编码器);JDY-31蓝牙模块;L298N电机驱动模块;杜邦线若干;1个面包板;

图片如下:

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

2、模块分析

        1、L298N电机驱动模块

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

        1.模块可驱动两路直流电机,输出A和B各接一直流电机即可;

        2.若使用12V供电,将12V供电端口及GND接上电源正负即可,同时5V供电端可以作为最小系统板的输入电源;

        3.若不需要使用PWM调速,只需要控制电机正反转,则逻辑A与B跳线帽插上即可,相当于始终使能;

        4.若需要使用PWM调速,需将跳线帽拔起,将使能端接上单片机IO口。(定时器IO口,PWM输出模式);

        5.逻辑输入四个端口IN1、IN2、IN3、IN4接单片机四个IO口,每两个端口控制的一路电机。

        温馨提示: 特别不建议新手或者资金有限的情况下,使用电机驱动模块直连成品开发板,很容易烧坏。

        原因:(1) 由于电机的特性,电机在堵转或者高负载下,电流会增大,可能会影响到单片机。(2)新手玩单片机可能出现短路等情况,很容易板子冒烟;

L298N的转动逻辑图:

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

        2、0.96寸OLED(I2C通讯)

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

        (1)目前市面主要分为OLED与LCD这2种屏幕;

        (2)OLED自发光特性,LCD都要背光,而OLED不需要,因为它是自发光。这样同样的显示,OLED效果要来得好一些;

        (3)多种接口方式:6800,8080两种并行接口方式,4线的穿行SPI接口,IIC接口方式(2线);

        (4)不要接过高电压,3.3V就可以正常工作了;

        (5)OLED不足之处是做大之后成本较高。

        本实验采用了0.96寸OLED的屏幕(通讯方式IIC),4个接线柱(SCL,SDA,GND,VCC);         IIC通讯实现方式: IIC(Inter-Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器及其外围设备。它是由数据线 SDA 和时钟 SCL 构成的串行总线,可发送和接收数据。高速 IIC 总线一般可达 400kbps 以上。

模拟IIC通讯:

        I2C 是支持多从机的,也就是一个 I2C 控制器下可以挂多个 I2C 从设备,这些不同的 I2C从设备有不同的器件地址,这样 I2C 主控制器就可以通过 I2C 设备的器件地址访问指定的 I2C设备了。SDA 和SCL 这两根线必须要接一个上拉电阻,一般是 4.7K。其余的 I2C 从器件都挂接到 SDA 和 SCL 这两根线上,这样就可以通过 SDA 和 SCL 这两根线来访问多个 I2C设备。

I2C 协议:(1)起始位;(2)停止位;(3)数据传输;(4)应答信号;(5)I2C 写时序;(6)I2C 读时序

I2C 写时序

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

1)、开始信号。

2)、发送 I2C 设备地址,每个 I2C 器件都有一个设备地址,通过发送具体的设备地址来决

定访问哪个 I2C 器件。这是一个 8 位的数据,其中高 7 位是设备地址,最后 1 位是读写位,为

1 的话表示这是一个读操作,为 0 的话表示这是一个写操作。

3)、 I2C 器件地址后面跟着一个读写位,为 0 表示写操作,为 1 表示读操作。

4)、从机发送的 ACK 应答信号。

5)、重新发送开始信号。

6)、发送要写写入数据的寄存器地址。

7)、从机发送的 ACK 应答信号。

8)、发送要写入寄存器的数据。

9)、从机发送的 ACK 应答信号。

10)、停止信号。

I2C 时序

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

I2C 单字节读时序比写时序要复杂一点,读时序分为 4 大步,第一步是发送设备地址,第二步是发送要读取的寄存器地址,第三步重新发送设备地址,最后一步就是 I2C 从器件输出要读取的寄存器值,我们具体来看一下这几步。

1)、主机发送起始信号。

2)、主机发送要读取的 I2C 从设备地址。

3)、读写控制位,因为是向 I2C 从设备发送数据,因此是写信号。

4)、从机发送的 ACK 应答信号。

5)、重新发送 START 信号。

6)、主机发送要读取的寄存器地址。

7)、从机发送的 ACK 应答信号。

8)、重新发送 START 信号。

9)、重新发送要读取的 I2C 从设备地址。

10)、读写控制位,这里是读信号,表示接下来是从 I2C 从设备里面读取数据。

11)、从机发送的 ACK 应答信号。

12)、从 I2C 器件里面读取到的数据。

13)、主机发出 NO ACK 信号,表示读取完成,不需要从机再发送 ACK 信号了。

14)、主机发出 STOP 信号,停止 I2C 通信。

        3、JDY-31蓝牙模块

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

         市场上蓝牙模块有很多,常见的JDY-xx,HC-xx等系列。其实看似高级的蓝牙功能背后就是简单的串口通讯;  

        USART 的全称是 Universal Synchronous/Asynchronous Receiver/Transmitter,也就是同步/异步串行收发器。相比 UART 多了一个同步的功能,在硬件上体现出来的就是多了一条时钟线。一般 USART 是可以作为 UART 使用的,也就是不使用其同步的功能。

串口通讯协议:

        数据包:串口通讯的数据包由发送设备通过自身的TXD接口传输到接收设备得RXD接口,在协议层中规定了数据包的内容,具体包括起始位、主体数据(8位或9位)、校验位以及停止位,通讯的双方必须将数据包的格式约定一致才能正常收发数据。

具体如图所示:

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

        波特率:由于异步通信中没有时钟信号,所以接收双方要约定好波特率,即每秒传输的码元个数,以便对信号进行解码,常见的波特率有4800、9600、115200等。STM32中波特率的设置通过串口初始化结构体来实现。

        注意:MCU设置的波特率大小要与蓝牙APP设置的大小一致!

        4、6线减速电机(带编码器)模块:

        市面上电机有很多,常用的有步进电机,直流减速电机,伺服电机等等; 编码器:用来测量电机转速的仪器元件,常见的有:霍尔编码器,光电编码器等 电机的驱动原理很简单,给电压差即可使得电机转动,调速则利用PWM调节发。

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

        编码器原理: 编码器是一种将角位移或者角速度转换成一串电数字脉冲的旋转式传感器。 编码器工作原理: 霍尔编码器是有霍尔马盘和霍尔元件组成。霍尔马盘是在一定直径的圆板上等分的布置有不同的磁极。霍尔马盘与电动机同轴,电动机旋转时,霍尔元件检测输出若干脉冲信号,为判断转向,一般输出两组存在一定相位差的方波信号。

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

         注意:通过判断A与B相哪一位在前,即可判断出正转还是反转

二、CubexMX设置

        使用的MCU为stm32f103c8t6:

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

        RCC:

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

         SYS:

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

        注意:Debug这里一定要设置成Serial Wire否则可能出现芯片自锁

        GPIO设置:

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

        定时TIM2用来测速与测量正转反转(计数器模式)

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

         定时3:PWM调节

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

        I2C:

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32         USART1:

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

        之后按照自己习惯生成初始化文件

三、代码

        自动生成的:

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

        需要自己编写的:

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

        I2C代码:

#include "oled.h"
#include "asc.h"
#include "main.h"
void WriteCmd(unsigned char I2C_Command)//???
 {
	HAL_I2C_Mem_Write(&hi2c2,OLED0561_ADD,COM,I2C_MEMADD_SIZE_8BIT,&I2C_Command,1,100);
 }
		
void WriteDat(unsigned char I2C_Data)//???
 {
		HAL_I2C_Mem_Write(&hi2c2,OLED0561_ADD,DAT,I2C_MEMADD_SIZE_8BIT,&I2C_Data,1,100);
  }
	void OLED_Init(void)
{
	HAL_Delay(100); //????????
	
	WriteCmd(0xAE); //display off
	WriteCmd(0x20);	//Set Memory Addressing Mode	
	WriteCmd(0x10);	//00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
	WriteCmd(0xb0);	//Set Page Start Address for Page Addressing Mode,0-7
	WriteCmd(0xc8);	//Set COM Output Scan Direction
	WriteCmd(0x00); //---set low column address
	WriteCmd(0x10); //---set high column address
	WriteCmd(0x40); //--set start line address
	WriteCmd(0x81); //--set contrast control register
	WriteCmd(0xff); //???? 0x00~0xff
	WriteCmd(0xa1); //--set segment re-map 0 to 127
	WriteCmd(0xa6); //--set normal display
	WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
	WriteCmd(0x3F); //
	WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
	WriteCmd(0xd3); //-set display offset
	WriteCmd(0x00); //-not offset
	WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency
	WriteCmd(0xf0); //--set divide ratio
	WriteCmd(0xd9); //--set pre-charge period
	WriteCmd(0x22); //
	WriteCmd(0xda); //--set com pins hardware configuration
	WriteCmd(0x12);
	WriteCmd(0xdb); //--set vcomh
	WriteCmd(0x20); //0x20,0.77xVcc
	WriteCmd(0x8d); //--set DC-DC enable
	WriteCmd(0x14); //
	WriteCmd(0xaf); //--turn on oled panel
}

void OLED_SetPos(unsigned char x, unsigned char y) //???????
{ 
	WriteCmd(0xb0+y);
	WriteCmd(((x&0xf0)>>4)|0x10);
	WriteCmd((x&0x0f)|0x01);
}

void OLED_Fill(unsigned char fill_Data)//????
{
	unsigned char m,n;
	for(m=0;m<8;m++)
	{
		WriteCmd(0xb0+m);		//page0-page1
		WriteCmd(0x00);		//low column start address
		WriteCmd(0x10);		//high column start address
		for(n=0;n<128;n++)
			{
				WriteDat(fill_Data);
			}
	}
}


void OLED_CLS(void)//??
{
	OLED_Fill(0x00);
}

void OLED_ON(void)
{
	WriteCmd(0X8D);  //?????
	WriteCmd(0X14);  //?????
	WriteCmd(0XAF);  //OLED??
}

void OLED_OFF(void)
{
	WriteCmd(0X8D);  //?????
	WriteCmd(0X10);  //?????
	WriteCmd(0XAE);  //OLED??
}

// Parameters     : x,y -- ?????(x:0~127, y:0~7); ch[] -- ???????; TextSize -- ????(1:6*8 ; 2:8*16)
// Description    : ??codetab.h??ASCII??,?6*8?8*16???
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
{
	unsigned char c = 0,i = 0,j = 0;
	switch(TextSize)
	{
		case 1:
		{
			while(ch[j] != '\0')
			{
				c = ch[j] - 32;
				if(x > 126)
				{
					x = 0;
					y++;
				}
				OLED_SetPos(x,y);
				for(i=0;i<6;i++)
					WriteDat(F6x8[c][i]);
				x += 6;
				j++;
			}
		}break;
		case 2:
		{
			while(ch[j] != '\0')
			{
				c = ch[j] - 32;
				if(x > 120)
				{
					x = 0;
					y++;
				}
				OLED_SetPos(x,y);
				for(i=0;i<8;i++)
					WriteDat(F8X16[c*16+i]);
				OLED_SetPos(x,y+1);
				for(i=0;i<8;i++)
					WriteDat(F8X16[c*16+i+8]);
				x += 8;
				j++;
			}
		}break;
	}
}

// Parameters     : x,y -- ?????(x:0~127, y:0~7); N:???.h????
// Description    : ??ASCII_8x16.h????,16*16??
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
{
	unsigned char wm=0;
	unsigned int  adder=32*N;
	OLED_SetPos(x , y);
	for(wm = 0;wm < 16;wm++)
	{
		WriteDat(F16x16[adder]);
		adder += 1;
	}
	OLED_SetPos(x,y + 1);
	for(wm = 0;wm < 16;wm++)
	{
		WriteDat(F16x16[adder]);
		adder += 1;
	}
}

// ????????????????,????????“??——???——????”??????ascll.h?????(????)
//???????:x:?????  
//								y:???(??0-7)  
//								begin:????????????????ascll.c???????  
//                num:????????
//                ????“??”,??????????????????0,1,???0,??????,??:x:0,y:2,begin:0,num:2
void OLED_ShowCN_STR(u8 x , u8 y , u8 begin , u8 num)
{
	u8 i;
	for(i=0;i<num;i++){OLED_ShowCN(i*16+x,y,i+begin);}    //OLED????
}

// Parameters     : x0,y0 -- ?????(x0:0~127, y0:0~7); x1,y1 -- ?????(???)???(x1:1~128,y1:1~8)
// Description    : ??BMP??
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{
	unsigned int j=0;
	unsigned char x,y;

  if(y1%8==0)
		y = y1/8;
  else
		y = y1/8 + 1;
	for(y=y0;y<y1;y++)
	{
		OLED_SetPos(x0,y);
    for(x=x0;x<x1;x++)
		{
			WriteDat(BMP[j++]);
		}
	}
}

void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
{      	
	unsigned char c=0,i=0;	
		c=chr-' ';//???????			
		if(x>128-1){x=0;y=y+2;}
		if(Char_Size ==16)
			{
			OLED_SetPos(x,y);	
			for(i=0;i<8;i++)
			WriteDat(F8X16[c*16+i]);
			OLED_SetPos(x,y+1);
			for(i=0;i<8;i++)
			WriteDat(F8X16[c*16+i+8]);
			}
			else {	
				OLED_SetPos(x,y);
				for(i=0;i<6;i++)
				WriteDat(F6x8[c][i]);
				
			}
}
u32 oled_pow(u8 m,u8 n)
{
	u32 result=1;	 
	while(n--)result*=m;    
	return result;
}	
//??2???
//x,y :????	 
//len :?????
//size:????
//mode:??	0,????;1,????
//num:??(0~4294967295);	 		  
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2)
{         	
	u8 t,temp;
	u8 enshow=0;						   
	for(t=0;t<len;t++)
	{
		temp=(num/oled_pow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
				OLED_ShowChar(x+(size2/2)*t,y,' ',size2);
				continue;
			}else enshow=1; 
		}
	 	OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2); 
	}
} 

         UART代码:

#include "uart.h"

uint8_t USART1_RX_BUF[USART1_REC_LEN];//????,??USART_REC_LEN???.
uint16_t USART1_RX_STA=0;//??????//bit15:??????,bit14~0:??????????
uint8_t USART1_NewData;//?????????1????????

extern int flag;

void  HAL_UART_RxCpltCallback(UART_HandleTypeDef  *huart)//????????
{
    if(huart ==&huart1)
    {
        if((USART1_RX_STA&0x8000)==0)//?????
        {
            if(USART1_NewData==0x5A)//????0x5A
            {
                 USART1_RX_STA|=0x8000;   //?????,?USART2_RX_STA??bit15(15?)?1
            }
            else
            {
                   USART1_RX_BUF[USART1_RX_STA&0X7FFF]=USART1_NewData; 

							if(USART1_RX_BUF[1] == 0x01)
							{
								flag = 2;
							}
							
                   USART1_RX_STA++;  //???????1
                   if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//??????,??????

            }
        }
        HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1); 

    }
}

         常规的编写如上,但是本人的MCU存在问题,单片机并未接收到预设的数据。

        所以,本人项目中采用了下方代码:

#include "uart.h"

uint8_t USART1_RX_BUF[USART1_REC_LEN];//????,??USART_REC_LEN???.
uint16_t USART1_RX_STA=0;//??????//bit15:??????,bit14~0:??????????
uint8_t USART1_NewData;//?????????1????????

extern int flag;

void  HAL_UART_RxCpltCallback(UART_HandleTypeDef  *huart)//????????
{
    if(huart ==&huart1)
    {
                              
              USART1_RX_BUF[USART1_RX_STA&0X7FFF]=USART1_NewData; 					
              USART1_RX_STA++;  //???????1
			
                   if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//??????,??????
			
							if(USART1_RX_BUF[USART1_RX_STA-4] == 0xA0)
							{
								flag = 1;
							}
							if(USART1_RX_BUF[USART1_RX_STA-4] == 0x90)
							{
								flag = 2;
							}
							if(USART1_RX_BUF[USART1_RX_STA-4] == 0xD0)
							{
								flag = 3;
							}
							if(USART1_RX_BUF[USART1_RX_STA-4] == 0x88)
							{
								flag = 4;
							}
							if(USART1_RX_BUF[USART1_RX_STA-4] == 0x48)
							{
								flag = 5;
							}							
        
        HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1); 

    }
}

         如果大家自己使用的花,可以根据自己的蓝牙APP写这段程序,有问题欢迎留言

        Motor代码:

#include "motor.h"

void MOTOR_GO()
{
	__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1,3000);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
}

void MOTOR_BACK()
{
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
}

void MOTOR_STOP()
{
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
}

void MOTOR_UP()
{
	__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1,1);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
}

void MOTOR_DOWN()
{
	__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1,400);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
}

         PID:

PID算法:

PID分为位置型和增量型

增量型即通过 u(k)-u(k-1) 从而得出式子:

公式的第一部分是比例式 是为了让值按一定比例达到目标值;

第二部分是积分值,正值,在计算的过程中往往会受到环境等一些其他因素的影响,导致值不能到达目标值;

第三部分是微分值,通常是负值,后一次偏差值往往小于前一次偏差值,目的是为了防止值增加过大,通常起一个阻碍的作用;

直流减速电机的控制,STM32开发,c语言,嵌入式硬件,stm32

        PID代码:

#include "pid.h"
#include "tim.h"
#include "main.h"
#include "math.h"
#include "i2c.h"
#include "oled.h"

unsigned int MotorSpeed;		//È«¾Ö±äÁ¿£¬µç»úµ±Ç°×ªËÙ
int SpeedTarget = 750;				//Ä¿±êתËÙ
int MotorOutput;						//µç»úÊä³ö

//1.ÀûÓÃTIM2¼ÆËãµç»úתËÙ

void GetMotorSpeed(void)
{
//		int CaptureNumber =	(short)__HAL_TIM_GET_COUNTER(&htim2);	  //HAL¿âº¯Êý¼ÆËãÂö³å´ÎÊý
//	
//	//µç»úתËÙת»»Speed=1sÄÚµÄÂö³åÊý/44(һȦ11¸öÐźţ¬4±¶Æµ·¨)/34¼õËÙ±È
//		int MotorSpeed=CaptureNumber*20/44/34*2*3.14*3;
//		OLED_ShowNum(40,0,MotorSpeed,4,16);
//	
//		__HAL_TIM_GET_COUNTER(&htim2) = 0;													//¼ÆÊýÆ÷ÇåÁã
	
	int CaptureNumber =	(short)__HAL_TIM_GET_COUNTER(&htim2);	  //???????
	__HAL_TIM_GET_COUNTER(&htim2) = 0;
//	int Speed=CaptureNumber*5/44/34*2*3.14*3;
    int 	Direction = __HAL_TIM_IS_TIM_COUNTING_DOWN(&htim2); 		
		if(Direction == 1)
		{
				CaptureNumber -= 65535;
		}
	MotorSpeed=CaptureNumber;
	OLED_ShowNum(40,0,MotorSpeed,4,16);
	HAL_Delay(100);
	OLED_CLS();
//	__HAL_TIM_GET_COUNTER(&htim2) = 0;
}

//2.ÔöÁ¿Ê½PID¿ØÖÆÆ÷£¨PID³£¼û·ÖΪλÖÃPIDºÍÔöÁ¿Ê½PID£©

int Error_Last,Error_Prev;		//ÉÏ´ÎÎó²î£¬ÉÏÉÏ´ÎÎó²î
int Pwm_add,Pwm;							//PWMÔöÁ¿,PWMÕ¼¿Õ±È

int Kp = 5, Ki = 3, Kd = 1;//PIDË㷨ϵÊý£¬¸¡µãÀàÐÍ£¬Ð¾Æ¬¼ÆËãÄÜÁ¦Ò»°ãʱ½¨ÒéÕûÐÍ£¬»òÕß*1024

int SpeedInnerControl(int Speed,int Target)		//ËÙ¶ÈÄÚ»·¿ØÖÆ
{
    int Error = Target - Speed;		//Îó²î = Ä¿±êËÙ¶È - ʵ¼ÊËÙ¶È 

    Pwm_add = Kp * (Error - Error_Last) + 										//±ÈÀý
							Ki * Error +																		//»ý·Ö
							Kd * (Error - 2.0f * Error_Last + Error_Prev);	//΢·Ö


    Pwm += Pwm_add;		//Êä³öÁ¿=ԭʼÁ¿+ÔöÁ¿
	
		Error_Prev = Error_Last;	//±£´æÉÏÉÏ´ÎÎó²î
    Error_Last = Error;				//±£´æÉÏ´ÎÎó²î

    if(Pwm > 4999) Pwm = 3000;	//ÏÞÖÆÉÏÏÂÏÞ£¬·ÀÖ¹PWM³¬³öÁ¿³Ì
    if(Pwm <-4999) Pwm =-3000;

    return Pwm;	//·µ»ØÊä³öÖµ
}

//3.µç»úתËÙÓë·½ÏòµÄº¯Êý£¨PID¿ØÖÆ£©

void SetMotorVoltageAndDirection(int Pwm)
{
    if(Pwm < 0)			//Èç¹ûPWMСÓÚ0
    {        
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
        
			Pwm = (-Pwm);			//PWMÖ»ÄÜÈ¡ÕýÖµ£¬Èç¹ûΪ¸ºÊý£¬Ö±½ÓÈ¡·´
        
			__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, Pwm);	//PWMµ÷ËÙ
			
    } else
    {
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
			
      __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, Pwm);	//PWMµ÷ËÙ
    }
}

void ModePID()
{
  GetMotorSpeed();
  MotorOutput = SpeedInnerControl(MotorSpeed,SpeedTarget);
  SetMotorVoltageAndDirection(MotorOutput);
}

         主函数代码:

 while (1)
 {  
    		 switch(flag)    
    		 {
    		 case(1):MOTOR_GO();break;
    		 case(2):MOTOR_BACK();break;
    		 case(3):MOTOR_STOP();break;
    		 case(4):MOTOR_UP();break;
    		 case(5):ModePID();break;
    		 default:break;
    		 }     
		
    /* USER CODE END WHILE */
		if(flag != 5)
		{
		int CaptureNumber =	(short)__HAL_TIM_GET_COUNTER(&htim2);	  //???????
		__HAL_TIM_GET_COUNTER(&htim2) = 0;
//    int 	Direction = __HAL_TIM_IS_TIM_COUNTING_DOWN(&htim2); 
		//µç»úתËÙת»»Speed=1sÄÚµÄÂö³åÊý/44(һȦ11¸öÐźţ¬4±¶Æµ·¨)/34¼õËÙ±È
//		int Speed=CaptureNumber*5/44/34*2*3.14*3;
    int 	Direction = __HAL_TIM_IS_TIM_COUNTING_DOWN(&htim2); 		
		if(Direction == 1)
		{
				CaptureNumber -= 65535;
		}
			
		int Speed=CaptureNumber;
    OLED_ShowNum(40,0,Speed,5,16);
		HAL_Delay(100);
		OLED_CLS();			
		}
		
    int 	Direction = __HAL_TIM_IS_TIM_COUNTING_DOWN(&htim2); 			
		OLED_ShowCN_STR(0,0,0,3);
//    OLED_ShowNum(40,0,Speed,4,16);
		OLED_ShowStr(90,0,"cm/s",2);
		OLED_ShowCN_STR(0,3,3,2);		
    if(Direction==0)
		{
			OLED_ShowCN_STR(40,3,5,2);	
		}
		if(Direction==1)
		{
			OLED_ShowCN_STR(40,3,7,2);
		}		
//		HAL_Delay(1000);
//		OLED_CLS();
    /* USER CODE BEGIN 3 */
  }

        蓝牙APP源代码以及技术论文:链接:https://pan.baidu.com/s/1-rbicxuyLVCq6rglCWcJTg 
提取码:huzm文章来源地址https://www.toymoban.com/news/detail-829954.html

到了这里,关于基于stm32的减速直流电机PID算法控制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 直流减速编码电机的使用(STM32f103c8t6)L298N电机驱动模块

    直接减速电机就是在直流电机上加上霍尔编码器,霍尔编码器可用于电机转动的测速,A、B相会产生相位相差90°的方波信号。stm32可以使用硬件资源或者软件模拟来捕获编码器信号。这里我介绍的是stm32自带的编码器模式来使用直流减速电机。 以下是直流减速电机的商品图 ​

    2024年02月13日
    浏览(33)
  • STM32增量式pid直流电机调速(内附源码)

            目录 一. 1.硬件组成 2.模块分析         1.TB6612电机驱动模块         2.直流减速电机         3.电源稳压模块 二.接线 三.代码思路讲解(详见源码) 四.STM32cubmx配置 1.系统基础配置:(重要) 2.电机旋转方向引脚配置 3.TIM1每10ms触发一次的定时器中断 4.TI

    2024年02月16日
    浏览(19)
  • PID模块化__以stm32直流电机速度为例

    本篇使用到的基于这个STM32CubeMX 直流电机PID速度控制、HAL库、cubemx、PID、速度控制、增量式 由于上次使用的pid没有模块化,当多出使用pid的时候就会很麻烦 所以这次使用的模块化的 在main.c或者其他位置创建pid的变量 注意一定要在pid计算之前初始化 all_moto_pid_init ,不然会导

    2024年02月14日
    浏览(13)
  • STM32CubeMX 直流电机串级PID位置速度控制、HAL库、cubemx、PID、串级PID、位置控制、速度控制、双环控制

    提示:本文章的串级PID位置速度控制,是在前两篇文章速度控制,位置控制的基础上实现的,这一章节中不需要额外的cubemx的配置,只需要写简单的代码即可,复杂的地方在于串级pid的调试过程。 pid是我们在学习单片机中首先要学会的控制算法,而串级pid又是在单pid的基础上

    2024年02月14日
    浏览(25)
  • 【32单片机学习】(3)霍尔编码器减速直流电机控制及测速

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 1.实验现象 2.实验接线及原理图 接线图 原理图  电机接线图 3.代码部分 1.主函数  main.c 2.按键部分   key.c  key.h pwm代码   pwm.c  pwm.h 电机驱动   motor.c   motor.h  OLED显示 oled.c oled.h  编码器

    2024年02月11日
    浏览(27)
  • 基于51单片机的直流电机转速显示+加速减速启停

    做了一个实战项目,这个实战项目主要是实现对直流电机转速的控制,可以实现电机加速,减速,报警、启停以及显示转速。在本电路的基础上也可以进行一些拓展改变电机正反转的状态,只需要外加一个按键和修改部分程序即可,在文章的最后会对拓展进行一个说明。基础

    2023年04月27日
    浏览(20)
  • 基于STM32的智能巡检小车系统设计--STM32最小系统、直流电机、直流电源模块设计

    作者:车 邮箱:692604135@qq.com 学校:西安工程大学硕士研究生 方向:机器视觉、图像分割、深度学习 在介绍具体实现功能之前,需要介绍以下模块。 本课题选择的单片机是ST(意法半导体)开发的STM32F407VET6。 这是一款采用Corte-M4为内核的高性能32位ARM微控制器。该芯片支持

    2024年02月10日
    浏览(17)
  • 基于STM32的300W无刷直流电机驱动方案

    近些年,由于无刷直流电机大规模的研发和技术的逐渐成熟,已逐步成为工业用电机的发展主流。围绕降低生产成本和提高运行效率,各大厂商也提供不同型号的电机以满足不同驱动系统的需求。现阶段已经在纺织、冶金、印刷、自动化生产流水线、数控机床等工业生产方面

    2024年02月12日
    浏览(17)
  • 基于STM32单片机的直流电机PWM调速(数码管显示)(Proteus仿真+程序)

          由 STM32单片机+数码管显示模块+键盘模块+L298N电机驱动模块+直流电机 1、采用STM32F103单片机为主控制器 2、四个按键,分别为启动/暂停、方向切换、加速、减速功能 3、数码管显示PWM占空比和电机转动方向(0正转,1反转) 注意:proteus8.11版本才能打开   24、基于STM32单

    2024年02月11日
    浏览(23)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包