一、实现的功能
- 能够接收串口输入的数据
- 数据范围设定为 0 — 65535,并设置超出范围设置错误提示
- 利用freertos任务向电机发送数据
- 利用串口控制电机位置
二、功能实现
省略基础配置
首先构建电机增量式位置环
float PID_Increase(Error sptr, PID_increase pid, float NowPlace, float Point)
{
float iError,//当前误差
Increase; //最后得出的实际增量
iError = Point - NowPlace; // 计算当前误差
Increase = pid.P * (iError - sptr.Last_Error) //比例P
+ pid.I * iError //积分I
+ pid.D * (iError - 2 * sptr.Last_Error + sptr.Previous_Error); //微分D
sptr.Previous_Error = sptr.Last_Error; // 更新前次误差
sptr.Last_Error = iError; // 更新上次误差
return Increase; // 返回增量
}
配置pid参数
typedef struct PID_INCREASE
{
float P;
float I;
float D;
}PID_increase;
PID_increase increase6020;
最后得出赋给电机的电压值
current_6020 = PID_Increase( error6020 , increase6020 , GM6020_Receive[0].angel, angel);
//其中 angel为
配置串口收发程序(使用USART6)
收发原理具体看上一篇文章
《STM32串口收发》
int x;
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART6->SR&0X40)==0);//循环发送,直到发送完毕
USART6->DR = (u8) ch;
return ch;
}
#endif
u8 USART_RX_BUF[USART_REC_LEN];
u16 USART_RX_STA=0; //接收状态标记
void USART6_Init(u32 bound) //打印使用
{
GPIO_InitTypeDef gpio;
USART_InitTypeDef usart;
NVIC_InitTypeDef nvic;
//USART3时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6,ENABLE);
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOG, ENABLE);
//串口3复用功能配置
GPIO_PinAFConfig(GPIOG,GPIO_PinSource9,GPIO_AF_USART6);
GPIO_PinAFConfig(GPIOG,GPIO_PinSource14,GPIO_AF_USART6);
//USART3-->PD8,PD9
GPIO_StructInit( &gpio);
gpio.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_14;
gpio.GPIO_Mode = GPIO_Mode_AF;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_Speed = GPIO_Speed_100MHz;
gpio.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init( GPIOG, &gpio);
//串口初始化
USART_DeInit(USART6);
usart.USART_BaudRate = bound;
usart.USART_WordLength = USART_WordLength_8b;
usart.USART_StopBits = USART_StopBits_1;
usart.USART_Parity = USART_Parity_No; //无校验
usart.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //收发模式
usart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART6,&usart); //串口3初始化
USART_Cmd(USART6,ENABLE); //串口3使能
USART_ClearFlag(USART6, USART_FLAG_TC);
USART_ITConfig(USART6, USART_IT_RXNE, ENABLE);//串口3接收非空中断使能
//USART3 NVIC 配置
nvic.NVIC_IRQChannel = USART6_IRQn; //串口3中断通道
nvic.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级3
nvic.NVIC_IRQChannelSubPriority = 2; //子优先级2
nvic.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&nvic); //根据指定的参数初始化NVIC寄存器
}
/*************串口数据处理函数*********商洪涛2020.11.3*********************************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
USART_SendData(pUSARTx,ch);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET)
{
// printf("Error:Can't get USART_FLAG_TXE");
}
}
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
unsigned int k=0;
do
{
Usart_SendByte( pUSARTx, *(str + k) );
k++;
} while(*(str + k)!='\0');
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
{}
}
/*****************************串口数据处理函数*****end*********************************/
void USART6_IRQHandler(void) //串口6中断服务程序
{
u8 Res,i;
if(USART_GetITStatus(USART6, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART6);//(USART1->DR); //读取接收到的数据
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else
{USART_RX_STA|=0x8000; //接收完成了
x = 0;
x = atoi(USART_RX_BUF);
for(i=0;i<200;i++)
{
USART_RX_BUF[i] = 0;
}
}
}
else //还没收到0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
USART.H
#define USART_REC_LEN 200 //定义最大接收字节数 200
#define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收
extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART_RX_STA; //接收状态标记
extern int x; //对接收的数据的整形类型
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void USART6_Init(u32 bound);
写中断函数计算闭环赋值电压
int counting = 0;
void TIM5_IRQHandler(void) //中断服务函数
{
if(TIM_GetITStatus(TIM5,TIM_IT_Update)==SET) //溢出中断
{
FreeRtosRuntimeticks++; //任务时间统计时基
count++;
if(count >= 20) //降低计算频率
{
YAW_spin(x); //x为串口接收到的值
count = 0;
}
}
TIM_ClearITPendingBit(TIM5,TIM_IT_Update);
}
创建freertos任务
#define TASK_MOTOR_DEPTH 120 //堆栈
#define TASK_MOTOR_PRIO 3 //优先级
TaskHandle_t TASK_MOTOR_Handle;//任务句柄
void Task_MOTOR( void * pvParameters)//任务函数
{
while(1)
{
taskENTER_CRITICAL(); //避免CAN加载函数被打断,故加入了临界保护段代码
data_load_GM6020_pitch(current_6020);
taskEXIT_CRITICAL();
vTaskDelay(5);
}
}
//创建任务
xTaskCreate( (TaskFunction_t) Task_MOTOR,
(const char *) "Task_MOTOR", /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
(uint16_t) TASK_MOTOR_DEPTH,
(void *) NULL,
(UBaseType_t) TASK_MOTOR_PRIO,
(TaskHandle_t *) &TASK_MOTOR_Handle );
三、功能演示
利用freertos实现串口控制CAN通信电机