实现功能:
利用PC控制步进电机转动。控制电机转动方向、转动速度、电机ENA以及读取转动角度
硬件清单:
1、单片机最小系统(本案例使用Atmega16芯片)
2、步进电机(二相四线)
3、稳压电源(24V)
4、步进电机驱动器(TB6600)
5、USB转TTL数据线
实物连接图:
原理图:
软件:
ICCV7 FOR AVR-写程序
Progisp-烧程序
VB6.0-写上位机程序
程序下载地址:
https://download.csdn.net/download/ludantongxue/11133814
单片机程序:
//头文件
#include
#include//SEI()函数_NOP()BIT();
#define uint unsigned int
#define uchar unsigned char
//步进电机控制变量
uchar bjfangxiang=0;//步进电机方向
uint bjzhuansuxishu[10]={0x00,0XDE3F,0XEF1F,0XF4BF,0XF78F,0XF93F,0XFA5F,0XFB2D,0XFBC7,0XFC3F};//步进电机转速数组/64分频状态下0.75RPM x1-9
uint bjzhuansu;//步进电机转速变量
uint n0=500;//电机转动步数
uchar n0flag;//判断将N0值发给PC
uchar n0pc[3];//步进电机角度发送给PC
//串口接收变量
uchar buzhoutemp;//接收中断第一级步骤
uchar buzhoutemp1;//接收中断第二级步骤
//步进电机接口定义
#define ENA0 (PORTA &=~BIT(0))
#define ENA1 (PORTA |=BIT(0))
#define DIR0 (PORTA &=~BIT(1))
#define DIR1 (PORTA |=BIT(1))
#define PUL0 (PORTA &=~BIT(2))
#define PUL1 (PORTA |=BIT(2))
//PORTD ^=BIT(2);取反操作
//********IO口初始化START**************************************************
void port_init(void)
{
PORTA = 0xFF;
DDRA = 0xFF;
PORTB = 0xFF;
DDRB = 0xFF;
PORTC = 0xFF;
DDRC = 0xFF;
PORTD = 0xFF;
DDRD = 0x02; //串口通讯
}
//***************口初始化END**************************************************
//延时函数
void delay_1ms(void)
{
unsigned int i;
for(i=1;i<(unsigned int)(11.059*143-2);i++);//定义晶振频率
}
void delay(unsigned int n)//延时微妙级
{
unsigned int i;
for(i=0;i才能使用
#define SCLK1 (PORTB |=BIT(0))
#define RCLK0 (PORTB &=~BIT(1))
#define RCLK1 (PORTB |=BIT(1))
#define DAT0 (PORTB &=~BIT(2))
#define DAT1 (PORTB |=BIT(2))
unsigned char number[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//0~9
unsigned char location[]={0x00,0x01,0x02,0x04,0x08};//0~4
void shumaguan(unsigned char weizhi,unsigned char shuzi)
{ unsigned char i,num; //定义一个无字符串变量
num=number[shuzi];
for(i=0;i<8;i++) //for 循环,循环8次,把一个数变成二进制发送出去
{
SCLK0;
if((num&0x80)==0)
DAT0;
else
DAT1;
num=num<<1;
SCLK1; // 把595频率置高
}
num=location[weizhi];
for(i=0;i<8;i++) //for 循环,循环8次,把一个数变成二进制发送出去
{
SCLK0;// 把595 SCLK频率置低电平
if((num&0x80)==0)
DAT0;
else
DAT1;
num=num<<1;
SCLK1;
}
RCLK0;
_NOP();_NOP();_NOP();_NOP();_NOP(); //延时函数,系统自带
RCLK1;
}
//***********串口初始化START**************************************************
void uart0_init(void)//初始化
{
UCSRB = 0x00;//接受中断关闭
UCSRA = 0x02;//异步倍速
UCSRC = 0x06;//异步,8位数据位
UBRRL = 0x8F;//波特率9600,晶振频率11.0592
UBRRH = 0x00;
UCSRB = 0x98;//0x18接收使能+发送使能,0x98开接收中断
}
void uart0_send(unsigned char i)//发送数据
{
while(!(UCSRA&(1<='0'&&temp<='9')
{
VBdata[i]=temp;
i++;
}
else if(temp=='e')
{
VCdata=chartoint(VBdata);
//UDR='\0';
return(VCdata);
}
}
}*/
unsigned int chartoint(uchar i,uchar a[5])
{
unsigned int num;
unsigned char num1=a[0]-'0';
unsigned char num2=a[1]-'0';
unsigned char num3=a[2]-'0';
unsigned char num4=a[3]-'0';
unsigned char num5=a[4]-'0';
if(i==5)
num=num1*10000+num2*1000+num3*100+num4*10+num5;
else if(i==4)
num=num1*1000+num2*100+num3*10+num4;
else if(i==3)
num=num1*100+num2*10+num3;
else if(i==2)
num=num1*10+num2;
else if(i==1)
num=num1;
else
{};
return(num);
}
void time1_int(void)
{
TCCR1B=0X03;//定时器1产生256分频(0X01不分频(定时小于5.9毫秒),0X02-8分频(定时小于47毫秒),0X03-64分频(定时小于379毫秒),0X04-256分频(定时小于1510毫秒),0X05-1024分频(定时小于6000毫秒))
TCNT1=bjzhuansu;//0.75RPM//定时器1放初值。初值=65535-晶振频率/分频X定时时间
TIMSK|=0x00;//关定时器1 TIMSK|=BIT(2);//开定时器1
DIR1;//电机正方向
ENA0;//电机自由状态 ENA0;//电机锁紧
bjzhuansu=bjzhuansuxishu[1];//电机速度初始化
}
//主程序
void main(void)
{
CLI();//关中断
port_init();//IO口初始化
uart0_init();//串口初始化
time1_int();//定时器1初始化
SEI();//开中断
while(1)
{
shumaguan(1,n0%10);
delay(1);
shumaguan(2,n0%100/10);
delay(1);
shumaguan(3,n0/100);
delay(1);
if(n0flag==1)
{
n0flag=0;
n0pc[0]=n0/100;
uart0_send(n0pc[0]+'0');
n0pc[1]=n0%100/10;
uart0_send(n0pc[1]+'0');
n0pc[2]=n0%10;
uart0_send(n0pc[2]+'0');
}
}
}
#pragma interrupt_handler time1:9
void time1(void)
{
TCNT1=bjzhuansu;//步进电机转速调整
PORTA ^=BIT(2);
if((PINA & BIT(2))==0)//电机步数加减
{
n0flag=1;
if(bjfangxiang==1)n0++;
else if(bjfangxiang==0)n0--;//
}
}
#pragma interrupt_handler uart0_rx_isr:iv_USART0_RXC//接收中断
void uart0_rx_isr(void)//串口中断程序
{
uchar temp;
temp=UDR;
CLI();//关总中断
switch(buzhoutemp) //PC控制单片机
{
case 0:if(temp=='#'){buzhoutemp=1;}
else {buzhoutemp=0;}break;
case 1:buzhoutemp1=temp;buzhoutemp=2;break;//步进电机功能控制项
case 2:if(buzhoutemp1=='1')//电机转向选择
{
if(temp=='0'){DIR0;bjfangxiang=0;buzhoutemp=3;}
else if(temp=='1'){DIR1;bjfangxiang=1;buzhoutemp=3;}
else {buzhoutemp=0;}break;
}
else if(buzhoutemp1=='2')//电机转速选择
{
bjzhuansu=bjzhuansuxishu[temp-'0'];
buzhoutemp=3;
}
else if(buzhoutemp1=='3')//控制电机锁紧状态
{
if(temp=='0'){ENA1;buzhoutemp=3;}
else if(temp=='1'){ENA0;buzhoutemp=3;}
else {buzhoutemp=0;}break;
}
else if(buzhoutemp1=='4')//控制电机启停
{
if(temp=='0'){TIMSK=0x00;buzhoutemp=3;}
else if(temp=='1'){TIMSK|=BIT(2);buzhoutemp=3;}
else {buzhoutemp=0;}break;
}
else if(buzhoutemp1=='5')//清零步进电机步数
{
n0=500;//步数清零
buzhoutemp=3;
}
else {buzhoutemp=0;}break;
case 3:if(temp=='*'){buzhoutemp=0;}break;
default:buzhoutemp=0;break;
}
SEI();//开总中断
}
//***************串口中断END**************************************************
/*
a0=uart0_receive()-'0';
uart0_send(a0+'0');
*/
/*switch(buzhoutemp) //PC传数据到单片机
{
case 0:if(temp=='#'){buzhoutemp=1;}
else {jieshouflag=0;buzhoutemp=0;}break;
case 1:if(temp!='*'){jieshou[0]=temp;buzhoutemp=2;}
else {jieshouflag=1;}break;
case 2:if(temp!='*'){jieshou[1]=temp;buzhoutemp=3;}
else {jieshouflag=1;}break;
case 3:if(temp!='*'){jieshou[2]=temp;buzhoutemp=4;}
else {jieshouflag=1;}break;
case 4:if(temp!='*'){jieshou[3]=temp;buzhoutemp=5;}
else {jieshouflag=1;}break;
case 5:if(temp!='*'){jieshou[4]=temp;buzhoutemp=6;}
else {jieshouflag=1;}break;
case 6:if(temp=='*'){jieshouflag=1;}break;
default:buzhoutemp=0;break;
}*/
上位机程序-VB6.0:
Option Explicit
Dim a As String '电机角度中间变量
Private Sub Combo1_Click()
If Combo1.Text = "正" Then
MSComm1.Output = "#10*"
ElseIf Combo1.Text = "反" Then
MSComm1.Output = "#11*"
End If
End Sub
Private Sub Combo2_Click()
Dim temp As String
temp = "#2" & Combo2 & "*"
MSComm1.Output = temp
Print temp
End Sub
Private Sub Command1_Click()
''''''''''''''''''''自动开串口''''''''''''''''''''''''''''''''
Dim i As Integer
If MSComm1.PortOpen = True Then MSComm1.PortOpen = False
If Command1.Caption = "打开串口" Then
For i = 1 To 16
MSComm1.CommPort = i
On Error Resume Next
MSComm1.PortOpen = True
If MSComm1.PortOpen = False Then GoTo Exit1
Command1.Caption = "关闭串口" '"COM" & i & "已打开"
GoTo Exit2
Exit1:
Next i
ElseIf Command1.Caption = "关闭串口" Then
If MSComm1.PortOpen = True Then MSComm1.PortOpen = False
Command1.Caption = "打开串口"
End If
Exit2:
''''''''''''''''''''自动开串口''''''''''''''''''''''''''''''''
Frame1.Enabled = True
End Sub
Private Sub Command2_Click()
Text2.Text = 0
MSComm1.Output = "#50*"
End Sub
Private Sub Command4_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Command4.Caption = "转动" Then
Command4.Caption = "结束"
MSComm1.Output = "#41*"
ElseIf Command4.Caption = "结束" Then
Command4.Caption = "转动"
MSComm1.Output = "#40*"
End If
End Sub
Private Sub Command4_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Command4.Caption = "转动" Then
Command4.Caption = "结束"
MSComm1.Output = "#41*"
ElseIf Command4.Caption = "结束" Then
Command4.Caption = "转动"
MSComm1.Output = "#40*"
End If
End Sub
Private Sub Command6_Click()
If Command6.Caption = "电机释放" Then
Command6.Caption = "电机锁定"
MSComm1.Output = "#30*"
ElseIf Command6.Caption = "电机锁定" Then
Command6.Caption = "电机释放"
MSComm1.Output = "#31*"
End If
End Sub
Private Sub Form_Load()
''''''''''''''''''''串口初始化''''''''''''''''''''''''''''''''''
MSComm1.Settings = "9600,N,8,1" '波特率,无校验,8位数据,1位停止位
MSComm1.InBufferSize = 8 '设置返回接受缓冲区的大小,单位是字符
MSComm1.OutBufferSize = 8
If MSComm1.PortOpen = True Then MSComm1.PortOpen = False '关串口
MSComm1.RThreshold = 3 '设置并返回产生ONCOMM事件的字符数,字符为单位
MSComm1.SThreshold = 3 '接收缓冲区收到每一个字符都会使MSComm产生OMcomm事件
MSComm1.InputLen = 0 '设置从接受缓冲区读取的字数,为0读取整个缓冲区
MSComm1.InputMode = 0 '0以文本方式接收,为1则以二进制取回
'If MSComm1.PortOpen = False Then MSComm1.PortOpen = True '开串口
MSComm1.InBufferCount = 0 '清空接收缓冲区
End Sub
Private Sub MSComm1_OnComm()
a = MSComm1.Input
'If a <> "" Then Text1 = a
'Text2 = (Text1 - 500) * Combo3.Text
If a <> "" Then Text2 = (Val(a) - 500) * Combo3.Text
End Sub
微信ID:saskingku