⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 main.c

📁 使用ICCAVR编辑
💻 C
字号:
// edited by matthew, May. 30, 2007

/* MCU2的程序
// 功能:
//     1. 接收上位机的数据:方向、速度、位移、Kp、Ki、Kd
//     2. 进行闭环控制(PID控制)
//     3. 向MCU1发送闭环控制后的PWM占空比(脉宽);向上位机回传当前速度、位移,用图形显示
*/

#include <iom8v.h>
#include <macros.h>

#include "v_pid.h" // 进行PID控制的函数在此定义

//#define FORWARD    	 	0x01
//#define BACKWARD   	 	0x00

#define SendStart  	 	0x01	// 发送开始
#define SendOver   	 	0x00	// 发送结束

#define LENGTH     	 	7		// 下位机发送位数为7+1
#define MIDDISTANCE  	65536L	// 初始位移
#define DeadError       50L		// 死区范围,当目标位置小于DeadError时,电机停转。

unsigned char VelSendFlag=SendOver,		// 向下位机发送数据开始标志
		      uart_rece_flag=0;			// 接收上位机数据完成标志
unsigned char dir_flag= FORWARD;	    // 电机转动方向标志
unsigned char UartSendData[10]={0},	    // 发送给下位机和PC的数据
		      Rec_data[10]={0};			// 从PC接收到的数据
signed 	 int  Num_Speed = 0;			//  记录码盘返回的当前速度脉冲数
signed 	 int  temp_sint=0;				// 控制电机用的PWM值,对应占空比
long     TargetPosition;				// 目标位移
long     distance=MIDDISTANCE;		    // 已走位移

void port_init(void)
{
 PORTB = 0x00; //0000 0110
 DDRB  = 0x00; //0000 0110
 PORTC = 0x0C; //pc2,pc3--1,out
 DDRC  = 0x0C;
 PORTD = 0x20;
 DDRD  = 0x00;
}


//TIMER0 initialize - prescale:1024
// desired value: 20mSec
void timer0_init(void)
// timer0 20ms中断初始化,采样周期T=20ms
{
 TCCR0 = 0x00; //stop
 TCNT0 = 0x29; //set count
 TCCR0 = 0x05; //start timer
}


#pragma interrupt_handler timer0_ovf_isr:10
void timer0_ovf_isr(void)
// 名称:20ms溢出中断服务程序
// 实现功能:
//       1. 调用闭环控制函数
//       2. 向MCU1发送电机控制的PWM波占空比;同时,向上位机发送当前的速度值和位移值
{
 TCNT0     = 0x29;       //reload counter value
 Num_Speed = TCNT1;  	 //得到T1反馈回的20ms内的编码器脉冲数
 TCNT1H    = 0x00;		 //计数器1清0
 TCNT1L    = 0x00;
 
 distance+=Num_Speed;		// 更新已走位移
 TargetPosition-=Num_Speed;		// 更新剩余位移

 // ************ 需要调整这里的移位数,因为尚不知道电机最大转速下,每个周期的脉冲数 *************//
 // 测得电机在最大转速下,该脉冲数字在350~450之间
 Num_Speed>>=1;		// 速度脉冲数/2, 折算到0~255

 sPID.vi_FeedBack = Num_Speed;		// 储存当前速度值,速度最大就是255,再大就不可能实现

 //************************ 在这里调用PID函数***********************//
 temp_sint = v_PIDCalc(&sPID);			// 计算得到控制的PWM值,在10到550之间,对应的是占空比
 // 550 是由下位机中的ICR1设置的,对应PWM方式14的TOP值

 if(TargetPosition<=DeadError)		// 处理进入死区的情况
 {
  temp_sint=1;
 }
 
 //更新要发送的数据
 if(dir_flag==BACKWARD)
 {
 UartSendData[1]=temp_sint>>8;
 UartSendData[1]+=1<<7;//置方向位,为1为反转
 UartSendData[2]=temp_sint;
 }
 else if(dir_flag==FORWARD)
 {
 UartSendData[1]=temp_sint>>8;
 UartSendData[2]=temp_sint;
 }
 else 
 {
 UartSendData[1]=UartSendData[2]=0;
 }
 
 UartSendData[3]=Num_Speed>>8;
 UartSendData[4]=Num_Speed;

 UartSendData[5]=distance>>16;
 UartSendData[6]=distance>>8;
 UartSendData[7]=distance;
 
 //置发送标志位
 VelSendFlag=SendStart;
  
}

//TIMER1 initialize - prescale:Rising edge
// WGM: 0) Normal, TOP=0xFFFF
// desired value: 1Hz
// actual value: Out of range
void timer1_init(void)
// timer1用来记录一个20ms周期内,从编码盘得到的脉冲数,采用计数方式
{
 TCCR1B = 0x00; //stop
 TCNT1H = 0x00 /*INVALID SETTING*/; //setup
 TCNT1L = 0x00 /*INVALID SETTING*/;
 TCCR1B = 0x07; //start Timer
}


//UART0 initialize
// desired baud rate: 9600
// actual: baud rate:9600 (0.0%)
// char size: 8 bit
// parity: Disabled
void uart0_init(void)
{
 UCSRB = 0x00; //disable while setting baud rate
 UCSRA = 0x00;
 UCSRC = BIT(URSEL) | 0x06;
 UBRRL = 0x47; //set baud rate lo
 UBRRH = 0x00; //set baud rate hi
 UCSRB = 0x98;
}


#pragma interrupt_handler uart0_rx_isr:12
void uart0_rx_isr(void)
// 从上位机接收数据的中断
{
 //uart has received a character in UDR
 static unsigned char data,rece_num=0,i=0;
 data=UDR;
 if((data==0xAA)&&(rece_num==0))//判断第1字节,0XAA起始位判断
     {
	 TIMSK = 0x00; //timer interrupt sources
	 Rec_data[rece_num]=data;
     rece_num++;
     } 
 else if(rece_num>0)//第2字节到第8字节寄存到接收数据中
   {
	 Rec_data[rece_num]=data;
     rece_num++;
	 if(rece_num==8)//接收完毕,标志位置1
      {
	    rece_num=0; //准备接收下一桢
	    uart_rece_flag=1;
       }
    }
}

#pragma interrupt_handler int0_isr:2
void int0_isr(void)
{
 //external interupt on INT0
}

#pragma interrupt_handler int1_isr:3
void int1_isr(void)
{
 //external interupt on INT1
}

//call this routine to initialize all peripherals
void init_devices(void)
{
 //stop errant interrupts until set up
 CLI(); //disable all interrupts
 port_init();
 timer0_init();
 timer1_init();
 uart0_init();
 MCUCR = 0x0A;
 GICR  = 0xC0;
 TIMSK = 0x05; //timer interrupt sources
 SEI(); //re-enable interrupts
 		//all peripherals are now initialized
}


// *************** main ****************//
void main(void)
{
 char i=0;
 
 init_devices();
 PIDInit();	// 调用PID参数初始化函数
 TargetPosition=0x0000;
 UartSendData[0]=0xAA;		// 发送到下位机或PC的起始位
 
 while(1)
 {
  
 //---------------start  if( uart_rece_flag==1)
  if( uart_rece_flag==1)	// 接收完数据后进行数据处理
 	{
	 uart_rece_flag=0;		// 清接收完成标志

	 PIDInit();  // 防止每次运行时出现超调现象, 
	 			 // 因为上次运行结束后, sPID.vi_PreU 不为0,各变量均应该清0 
	 
	 dir_flag=Rec_data[1];   				   //电机方向
	 sPID.vi_Ref =Rec_data[2];				   //给定速度大小

	 TargetPosition=Rec_data[3];  			   //目标位置
	 TargetPosition<<=12; 
	 distance=MIDDISTANCE;					   //位移 			  

	 sPID.v_Ka=Rec_data[4];					   //参数Ka = Kp
	 sPID.v_Kb=Rec_data[5];					   //参数Kb =  T / Ti 
	 sPID.v_Kb=Rec_data[6];					   //参数Kc = ( Td / T )

	 TIMSK = 0x01; //timer interrupt sources
	 
/*********************目标位置处理原理*****************************
设定值为丝杠转过的转数,则电机需要转过的转数为:设定转数×25
则编码器输出的脉冲为:设定转数×25×500=设定转数×12500
在程序中的处理为:假高设定转数为temp,则temp=<<10,即temp=temp×1024。
TargetPosition=temp<<2;//4倍
TargetPosition+=temp<<3;//8倍
这样处理之后,TargetPosition的值为:设定转数×12×1024=设定转数×12288
(12500-12288)/12500=0.01696=%1.696,误差非常小。
实际测试时设定的转数也能非常准确的到达,表明上述误差可忽略不计。
*******************************************************************/

    }
 //---------------end  if( uart_rece_flag==1)
 
 
 //---------------start  if(VelSendFlag==SendStart)
 if(VelSendFlag==SendStart)		// 开始发送数据
 {
  VelSendFlag=SendOver;				// 清开始发送标志
  for(i=0;i<=LENGTH;i++)
  {
   while (!(UCSRA&(1<<UDRE)));		// 等待发送寄存器为空
   UDR=UartSendData[i];						 // 为空时,发送下一字节
  }
 }
 //---------------end  if(VelSendFlag==SendStart)
 }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -