IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 基于STM32 的直流电机PID调速 及TFTLCD屏幕显示速度值及速度曲线 -> 正文阅读

[嵌入式]基于STM32 的直流电机PID调速 及TFTLCD屏幕显示速度值及速度曲线

学习STM32有一段时间了,完整的做了一套系统,记录一下。

一、目标功能:1、STM32 输出PWM控制直流电机转速。

? ? ? ? ? ? ? ? ? 2、采用增量式PID控制方法调节电机转速。

? ? ? ? ? ? ? ? ? 3、电机的转速值 和转速曲线实时显示在TFTLCD屏幕上。

? ? ? ? ? ? ? ? ? 4、电机的转速值 和PWM值每0.2s从串口输出。

? ? ? ? ? ? ? ? ? 5、通过串口输入,可以随时写入所需要达到的转速以及调整PID参数值。

二、系统组成:

? ? ? ? ? ? ? ? ? 1、STM32 ZET6 正点原子战舰版(带有TFTLCD屏幕)

? ? ? ? ? ? ? ? ? 2、L298N 直流电机驱动器

? ? ? ? ? ? ? ? ? 3、1000线 AB相单片机用编码器

? ? ? ? ? ? ? ? ? 4、12V直流电机

? ? ? ? ? ? ? ? ? 5、12V直流电源

? ? ? ? ? ? ? ? ? 6、杜邦线及导线若干

? ? ? ? ? ? ? ? ??

三、开发环境: KEIL-MDK?ARM 软件

? ? ? ? ? ? ? ? ? ?STM32 CUBE MX软件

四、接线说明:1、PWM 采用STM32 TIM2 CH1输出 ,输出频率10K HZ。

? ? ? ? ? ? ? ? ? 2、STM32 TIM2 CH1接入L298N 的BLA端,输出PWM到电机控制器。

? ? ? ? ? ? ? ? ? 2、编码器AB相 输入接入单片机TIM3 采用编码器模式,编码器AB相输入接入TIM3 CHI1 及CH2。

? ? ? ? ? ? ? ? ?3、单片机PE2及PE3接入L298N 的IN1 及IN2。作为电机正反转控制信号。

? ? ? ? ? ? ? ? ?4、电机接入L298N 的out1 及out2.

? ? ? ? ? ? ? ? ?5、直流电源正极接入L298N +12V ,负极接入L298N GND并与单片机GND连接。

? ?五、MX CUBE部分配置:

? ? ? ? ? ? ? ? ?1、FSMC配置(PB0为背光接口,注意不要遗漏配置)

?2、TFTLCD驱动程序

? ? ? ?驱动程序借用了正点原子HAL库示例程序中的LCD.c及LCD.h文件。删除其中关于FSMC的配置程序行。

3、TIM2配置(产生PWM波,频率10KHz)

4、TIM3配置(编码器模式),用于编码器测速。

5、TIM6配置(定时0.2s) ,定时器中断用于每0.2s 计算转速值,输入PWM值。

?6、NVIC优先级配置:这里建议将串口优先级最高。实际运行中发现,如果串口中断优先级不是最高,偶尔会出现输入的转速及PID值不正确的现象。

?7、GPIO及串口配置:GPIO 口使用PE2 及PE3 用于控制电机正反转。串口配置略。

六、部分程序说明

1、LCD屏幕上速度网格绘制程序

2、增量式PID函数

typedef struct
{
?? ?float ActualSpeed ; ? ? ? ? ? ? ? ? ? ? ? ? ?//当前值
?? ?float error; ? ? ? ? ? ? ? ? ? ? ?//偏差
?? ?float lasterror; ? ? ? ? ? ? ? ? ?//前一次偏差
?? ?float last_lasterror; ? ? ? ? ? ? ?//前两次偏差
?? ?int_least16_t PWM; ? ? ? ? ? ? ? ? //输出PWM(有符号型)
?? ?int_least16_t PWM_ADD; ? ? ? ? ? ? //增量PWM(有符号型)? ??
}PID;

/* 增量式PID 算法 */
?int PID_SET(uint16_t actual_speed,uint16_t set_speed)
?{
?? ? ? ?pid.error = set_speed - actual_speed; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //设定值减当前值,计算本次偏差
?? ??
?? ? ? ?pid.PWM_ADD=KP*(pid.error-pid.lasterror)+KI*pid.error+KD*(pid.error-2.0f*pid.lasterror+pid.last_lasterror);
?? ??? ?
?? ? ? ?pid.PWM+=pid.PWM_ADD;
?? ??
?? ? ? ?pid.last_lasterror = pid.lasterror; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//将此次偏差保留为上上次偏差
?? ? ? ? ? ? ? ? ? ? ??
?? ? ? ?pid.lasterror = pid.error; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //将此次偏差保留为上次偏差
?? ??
?? ? if(pid.PWM > 7200) pid.PWM = 7200; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//限副 PWM100% 为7200
?? ? if(pid.PWM < -7200) pid.PWM = -7200;
?? ??
? ? ? ? return pid.PWM;
?}
?
?/* 增量式PID 参数初始化 */
?void PID_init(void)
? {
?? ? ?pid.lasterror=0;
?? ? ?pid.last_lasterror = 0;
?? ? ?pid.PWM = 0;
? }?

3、速度值转换函数:用于将得到的速度值存入数组,在LCD屏幕上显示。

/* 速度值转化函数 */
//速度值分解千、百、十、个位后转成16进制ASC码 最后加上单位r/min
void speed_change(short motorspeed)
{
?? ?if(motorspeed<0) motorspeed=(-motorspeed); ? ?//反转转速为负数,转换成正数
?? ?
?? ?motornum[0]= (motorspeed/1000)+0x30;
?? ?motornum[1] = (motorspeed%1000/100)+0x30;
?? ?motornum[2] = (motorspeed%1000%100/10)+0x30;
?? ?motornum[3] = (motorspeed%1000%100%10)+0x30;
?? ?motornum[4]=0x72;
?? ?motornum[5]=0x2F;
?? ?motornum[6]=0x6d;
?? ?motornum[7]=0x69;
?? ?motornum[8]=0x6e;? ?
}

4、串口取PID值函数:用于将从串口取到的PID值取出来。PID值可以为小数。

/* 串口输入取PID参数数据函数 */

double Get_PIDDate(void) ? ??
{
?? ?double date=0;
?? ?double dat[Uart1_Rx_Cnt];
?? ?uint8_t i,j,k,n;
?? ?for(i=1;i<Uart1_Rx_Cnt;i++) ? ?//判断小数点的位置
?? ?{
?? ??? ?if(RxBuffer[i]=='.')?
?? ??? ?{
?? ??? ??? ?n=i; ? //记录小数点位置 ?
?? ??? ? ? ?break;
?? ??? ?}
?? ??? ?else n=?? ?Uart1_Rx_Cnt-2;?? ? ? ? //如果没有输入小数点、则n= 数组位数减去PID开头 以及0x0a 0x0d ? ? ? ? ? ?? ?
?? ?}
?? ?for(j=1;j<n;j++) ? ? ? ? ? ? ? //小数点前的数求和
?? ?{
?? ??? ?dat[j]=(RxBuffer[j]-'0')*pow(10,n-j-1);
?? ??? ?date+=dat[j];
?? ?}

?? ?for(k=n+1;k<Uart1_Rx_Cnt-2;k++) ? //加小数点后的数,如果没有小数点此函数不运行
?? ?{
?? ??? ?dat[k]=(RxBuffer[k]-'0')*pow(10,n-k);
?? ??? ?date+=dat[k];
?? ?}
?? ?
?? ?return date;
}
5、串口取速度函数

/* 串口输入取速度数据函数 */
? int Get_SpeedDate(void)
? {
?? ? ?uint16_t date=0;
?? ? ?uint16_t dat[5];
?? ? ?uint8_t i;
?? ? ?for(i=1;i<Uart1_Rx_Cnt-2;i++) ? //位数减去S开头 以及0x0a 0x0d
?? ? ?{
?? ? ?dat[i]=(RxBuffer[i]-'0')*pow(10,Uart1_Rx_Cnt-3-i); ?//pow为几次方数学函数,将串口输入的数转化成十进制(-48为转化成十进制)存入数组dat
?? ? ?date+=dat[i]; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//各位累加
?? ? ?}
?? ? ?
?? ? ?return date;
??
? }

6、串口中断回调函数:写了一个小协议,S开头输入代表设定所需转速,p开头输入代表设定PID的KP值,i开头输入代表设定PID的KI值,d开头输入代表设定PID的KD值。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) ?
{
? ? ? ?
?? ? ??
? ? ? ?/*此函数有如下BUG: 当发送超过数量超过267,触发数据溢出后仍然能够接收到超出部分的数据并发送出去*/
?? ?if(Uart1_Rx_Cnt>=255)
?? ?{
?? ??? ?
?? ??? ?Uart1_Rx_Cnt=0;
?? ??? ?memset(RxBuffer,0x00,sizeof(RxBuffer)); ? ? ? ? ? ? ? ? ? //将数组RxBuffer所有位清零
?? ??? ?HAL_UART_Transmit(&huart1,(uint8_t*)"数据溢出",10,0xffff);
?? ??? ?
?? ?}
?? ?else?
?? ?{
?? ??? ?RxBuffer[Uart1_Rx_Cnt++]=aRxBuffer; ? ? ? ? ? ? ? ? ? ? ? //将接收到的数据依次放入数组
?? ??? ?if((RxBuffer[Uart1_Rx_Cnt-1]==0x0a)&&(RxBuffer[Uart1_Rx_Cnt-2]==0x0d)) ?//判断已接收完成
?? ??? ?{
?? ??? ??? ?switch (RxBuffer[0])
?? ??? ??? ?{
?? ??? ??? ??? ?case 0x73: ? ? ? ? ? //s开头输入的为速度值
?? ??? ??? ?
?? ??? ??? ??? ?Set_Speed=Get_SpeedDate(); ?//从串口接收数组中取出设定速度值
?? ??? ??? ? ? ?printf("设定的速度值SetSpeed=%d\r\n",Set_Speed);
?? ??? ??? ? ? ?HAL_UART_Transmit(&huart1,(uint8_t *)&RxBuffer,Uart1_Rx_Cnt,0xffff); ? //将接收到的数据发送出去
?? ??? ??? ? ? ?while(HAL_UART_GetState(&huart1)==HAL_UART_STATE_BUSY_TX); ? ? ? ? ? ? //检测已发送完成
?? ??? ??? ? ? ?Uart1_Rx_Cnt=0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //接收数清零
?? ??? ??? ? ? ?memset(RxBuffer,0x00,sizeof(RxBuffer)); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //接收数组清零
?? ??? ??? ??? ?break;
?? ??? ??? ?
?? ??? ??? ?
?? ??? ??? ??? ?case 0x70 : ? ? ? // p开头输入的为KP值
?? ??? ??? ?
?? ??? ??? ??? ?KP=Get_PIDDate(); ?//从串口接收数组中取出设定P值
?? ??? ??? ? ? ?printf("设定的P值KP=%f\r\n",KP);
?? ??? ??? ? ? ?HAL_UART_Transmit(&huart1,(uint8_t *)&RxBuffer,Uart1_Rx_Cnt,0xffff); ? //将接收到的数据发送出去
?? ??? ??? ? ? ?while(HAL_UART_GetState(&huart1)==HAL_UART_STATE_BUSY_TX); ? ? ? ? ? ? //检测已发送完成
?? ??? ??? ? ? ?Uart1_Rx_Cnt=0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //接收数清零
?? ??? ??? ? ? ?memset(RxBuffer,0x00,sizeof(RxBuffer)); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //接收数组清零
?? ??? ??? ??? ?break;
?? ??? ??? ?
?? ??? ??? ??? ?case 0x69: ? ? ? ? //i 开头的为KI值
?? ??? ??? ?
?? ??? ??? ??? ?KI=Get_PIDDate(); ?//从串口接收数组中取出设定P值
?? ??? ??? ? ? ?printf("设定的I值KI=%f\r\n",KI);
?? ??? ??? ? ? ?HAL_UART_Transmit(&huart1,(uint8_t *)&RxBuffer,Uart1_Rx_Cnt,0xffff); ? //将接收到的数据发送出去
?? ??? ??? ? ? ?while(HAL_UART_GetState(&huart1)==HAL_UART_STATE_BUSY_TX); ? ? ? ? ? ? //检测已发送完成
?? ??? ??? ? ? ?Uart1_Rx_Cnt=0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //接收数清零
?? ??? ??? ? ? ?memset(RxBuffer,0x00,sizeof(RxBuffer)); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //接收数组清零
?? ??? ??? ??? ?break;
?? ??? ??? ?
?? ??? ??? ??? ?case 0x64: ? ? ? ? // d开头的为KI值
?? ??? ??? ?
?? ??? ??? ??? ?KD=Get_PIDDate(); ?//从串口接收数组中取出设定P值
?? ??? ??? ? ? ?printf("设定的D值KD=%f\r\n",KD);
?? ??? ??? ? ? ?HAL_UART_Transmit(&huart1,(uint8_t *)&RxBuffer,Uart1_Rx_Cnt,0xffff); ? //将接收到的数据发送出去
?? ??? ??? ? ? ?while(HAL_UART_GetState(&huart1)==HAL_UART_STATE_BUSY_TX); ? ? ? ? ? ? //检测已发送完成
?? ??? ??? ? ? ?Uart1_Rx_Cnt=0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //接收数清零
?? ??? ??? ? ? ?memset(RxBuffer,0x00,sizeof(RxBuffer)); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //接收数组清零
?? ??? ??? ??? ?break;
?? ??? ??? ?
?? ??? ??? ?default: ?printf("输入的参数有误,请重新输入!"); break;
?? ??? ??? ?}
?? ??? ?}
?? ?}
?? ?HAL_UART_Receive_IT(&huart1,(uint8_t *)&aRxBuffer,1); ? ? ? ? ? ? ? ? ? ? ? ? ?//接收一次数据后再次开启接收中断
?? ?
}

7、定时器中断回调函数:实现屏幕显示速度值及曲线,输出PWM等功能。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
?? ?
?? ?
?? ?if (htim == (&htim6)) ? ? ? ? ? ? ? ? ? ? ? ? ?
?? ?{
?? ??? ?motorspeed = (short)__HAL_TIM_GET_COUNTER(&htim3)*0.075; ?//200ms内脉冲数转换成r/min(1000编码器。0.2秒收到的脉冲,编码器模式4倍频)
?? ??? ? ? ? ? ? ? ? ?//先将__HAL_TIM_GET_COUNTER(&htim3)的计数值强制转换为short(-32768到32767)型,假如反转,转速从65535开始向下计数
?? ??? ? ? ? ? ? ? ? ?// 转换成short后为-1,-2,-3...
?? ??? ?
?? ??? ?__HAL_TIM_SET_COUNTER(&htim3,0); //编码器计数器清零
?? ??? ?
?? ??? ? LCD_ShowString(10,20,210,16,16,"motorspeed="); //显示motorspeed=
?? ??? ?
?? ??? ? speed_change(motorspeed); ? ? ? ? ? ? ? ? ? ? //速度值写入数组
?? ??? ?
?? ??? ? LCD_ShowString(100,20,210,16,16,motornum); ? ?//将数组内的速度值显示出来
?? ??? ?
?? ??? ? pwmVal = ?PID_SET(abs(motorspeed),Set_Speed); ? ? ? ? ? ? ? //20ms中断,执行PWM_SET函数 返回的PWM值给pwmVal
?? ??? ?
?? ??? ?__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_1,pwmVal); ? ? ?//将pwmVal值传递给TIM2通道的PWM输出
?? ??? ?
?? ??? ? printf("PWMval=%d\r\n",pwmVal); ? ? ? ? ? ? ? ? ? ? ? ?//串口输出PWM值
?? ??? ?
?? ??? ? printf("motorspeed=%d r/min\r\n",motorspeed); ? ? ? ? ? ? ? ?//串口输出电机速度值
?? ??? ?
?? ??? ?LCD_DrawPoint(ntime++,motorspeed/Speed_n()); ? ? ? ? ? ? ? //屏幕显示速度点,最高4000r/min, 每一行代表4000/800=5r/min。
?? ??? ?
?? ??? ?
?? ??? ?if (ntime >= 479) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//列满后刷新屏幕
?? ??? ?{
?? ??? ??? ?ntime=1;
?? ??? ??? ?LCD_Clear(WHITE);
?? ??? ??? ?LCD_SpeedDrawLine();
?? ??? ?}
?? ??? ?
?? ?}
?? ?
?? ?
}

8、其它:略。

七、效果视频

STM32直流电机PID调速

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-17 15:33:32  更:2021-08-17 15:34:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 2:05:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计