单片机控制步进电机程序

单片机控制步进电机程序 如何使用单片机精确控制步进电机?

如何使用单片机精确控制步进电机?

如何使用单片机精确控制步进电机?

方案一:Arduino套件。电脑可以通过串口与Arduino通信,然后再通过Arduino控制步进电机。方案二:树莓派。电脑不但可以通过串口与树莓派进行通信,还可以通过网络进行远程控制。方案三:STM32等单片机。依然是通过串口进行通信,功能相对来说最少,但也应该是最简单的。方案优劣对比,Arduino对于步进电机的控制更加方便,功能强大,树莓派相对来说复杂一些,而且很多库是用python写的,但对于网络远程控制更加有优势。如果仅需要控制一个步进电机,推荐使用串口直接控制单片机就好。以上,扯了一圈蛋,并没有什么实际的操作哈哈哈哈哈哈。

单片机是如何控制步进电机扎旋转的?

结合按键程序,我们设计这样一个功能程序:按数字键 1~9,控制电机转过 1~9 圈;配合上下键改变转动方向,按向上键后正向转 1~9 圈,向下键则反向转 1~9 圈;左键固定正转 90 度,右键固定反转 90;Esc 键终止转动。通过这个程序,我们也可以进一步体会到如何用按键来控制程序完成复杂的功能,以及控制和执行模块之间如何协调工作,而你的编程水平也可以在这样的实践练习中得到锻炼和提升。

#include

sbit KEY_IN_1 = P2^4

sbit KEY_IN_2 = P2^5

sbit KEY_IN_3 = P2^6

sbit KEY_IN_4 = P2^7

sbit KEY_OUT_1 = P2^3

sbit KEY_OUT_2 = P2^2

sbit KEY_OUT_3 = P2^1

sbit KEY_OUT_4 = P2^0

unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的映射表

{ 0x31, 0x32, 0x33, 0x26 }, //数字键 1、数字键 2、数字键 3、向上键

{ 0x34, 0x35, 0x36, 0x25 }, //数字键 4、数字键 5、数字键 6、向左键

{ 0x37, 0x38, 0x39, 0x28 }, //数字键 7、数字键 8、数字键 9、向下键

{ 0x30, 0x1B, 0x0D, 0x27 } //数字键 0、ESC 键、 回车键、 向右键

}

unsigned char KeySta[4][4] = { //全部矩阵按键的当前状态

{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}

}

signed long beats = 0 //电机转动节拍总数

void KeyDriver()

void main(){

EA = 1 //使能总中断

TMOD = 0x01 //设置 T0 为模式 1

TH0 = 0xFC //为 T0 赋初值 0xFC67,定时 1ms

TL0 = 0x67

ET0 = 1 //使能 T0 中断

TR0 = 1 //启动 T0

while (1){

KeyDriver() //调用按键驱动函数

}

}

/* 步进电机启动函数,angle-需转过的角度 */

void StartMotor(signed long angle){

//在计算前关闭中断,完成后再打开,以避免中断打断计算过程而造成错误

EA = 0

beats = (angle * 4076) / 360 //实测为 4076 拍转动一圈

EA = 1

}

/* 步进电机停止函数 */

void StopMotor(){

EA = 0

beats = 0

EA = 1

}

/* 按键动作函数,根据键码执行相应的操作,keycode-按键键码 */

void KeyAction(unsigned char keycode){

static bit dirMotor = 0 //电机转动方向

//控制电机转动 1-9 圈

if ((keycode>=0x30) (keycode 0){ //节拍数大于 0 时正转

index //正转时节拍输出索引递增

index = index 0x07 //用操作实现到 8 归零

beats-- //正转时节拍计数递减

}else{ //节拍数小于 0 时反转

index-- //反转时节拍输出索引递减

index = index 0x07 //用操作同样可以实现到-1 时归 7

beats //反转时节拍计数递增

}

tmp = P1 //用 tmp 把 P1 口当前值暂存

tmp = tmp 0xF0 //用操作清零低 4 位

tmp = tmp | BeatCode[index] //用|操作把节拍代码写到低 4 位

P1 = tmp //把低 4 位的节拍代码和高 4 位的原值送回 P1

}else{ //节拍数为 0 则关闭电机所有的相

P1 = P1 | 0x0F

}

}

/* T0 中断服务函数,用于按键扫描与电机转动控制 */

void InterruptTimer0() interrupt 1{

static bit p = 0

TH0 = 0xFC //重新加载初值

TL0 = 0x67

KeyScan() //执行按键扫描

//用一个静态 bit 变量实现二分频,即 2ms 定时,用于控制电机

p = ~p

if (p == 1){

TurnMotor()

}

}针对电机要完成正转和反转两个不同的操作,我们并没有使用正转启动函数和反转启动函数这么两个函数来完成,也没有在启动函数定义的时候增加一个形式参数来指明其方向。我们这里的启动函数 void StartMotor(signed long angle)与单向正转时的启动函数唯一的区别就是把形式参数 angle 的类型从 unsigned long 改为了 signed long,我们用有符号数固有的正负特性来区分正转与反转,正数表示正转 angle 度,负数就表示反转 angle 度,这样处理是不是很简洁又很明了呢?而你对有符号数和无符号数的区别用法是不是也更有体会了?

针对终止电机转动的操作,我们定义了一个单独的 StopMotor 函数来完成,尽管这个函数非常简单,尽管它也只在 Esc 按键分支内被调用了,但我们仍然把它单独提出来作为了一个函数。而这种做法就是基于这样一条编程原则:尽可能用单独的函数来完成硬件的某种操作,当一个硬件包含多个操作时,把这些操作函数组织在一起,形成一个对上层的统一接口。这样的层次化处理,会使得整个程序条理清晰,既有利于程序的调试维护,又有利于功能的扩充。

中断函数中要处理按键扫描和电机驱动两件事情,而为了避免中断函数过于复杂,我们就又分出了按键扫描和电机驱动两个函数(这也同样符合上述 2 的编程原则),而中断函数的逻辑就变得简洁而清晰了。这里还有个矛盾,就是按键扫描我们选择的定时时间是 1ms,而本章之前的实例中电机节拍持续时间都是 2ms;很显然,用 1ms 的定时可以定出 2ms 的间隔,而用 2ms 的定时却得不到准确的 1ms 间隔;所以我们的做法就是,定时器依然定时 1ms,然后用一个 bit 变量做标志,每 1ms 改变一次它的值,而我们只选择值为 1 的时候执行一次动作,这样就是 2ms 的间隔了;如果我要 3ms、4ms„„呢,把 bit 改为 char 或 int 型,然后对它们递增,判断到哪个值该归零,就可以了