[经验分享] 步进电机控制程序升级版本

[复制链接]
 楼主| 1988020566 发表于 2023-12-23 12:32 | 显示全部楼层 |阅读模式
设计这样一个功能程序:按数字键1~9,控制电机转过1~9圈;配合上下键改变转动方向,按向上键后正向转1~9圈,向下键则反向转1~9圈;

左键固定正转90度,右键固定反转90;Esc 键终止转动。通过这个程序,我们也可以进一步体会到如何用按键来控制程序完成复杂的功能,以及控制和执行模块之间如何协调工作,而你的编程水平也可以在这样的实践练习中得到锻炼和提升。

  1. #include <reg52.h>

  2. sbit KEY_IN_1 = P2^4;
  3. sbit KEY_IN_2 = P2^5;
  4. sbit KEY_IN_3 = P2^6;
  5. sbit KEY_IN_4 = P2^7;
  6. sbit KEY_OUT_1 = P2^3;
  7. sbit KEY_OUT_2 = P2^2;
  8. sbit KEY_OUT_3 = P2^1;
  9. sbit KEY_OUT_4 = P2^0;

  10. unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的映射表
  11.     { 0x31, 0x32, 0x33, 0x26 }, //数字键1、数字键2、数字键3、向上键
  12.     { 0x34, 0x35, 0x36, 0x25 }, //数字键4、数字键5、数字键6、向左键
  13.     { 0x37, 0x38, 0x39, 0x28 }, //数字键7、数字键8、数字键9、向下键
  14.     { 0x30, 0x1B, 0x0D, 0x27 } //数字键0、ESC 键、 回车键、 向右键
  15. };
  16. unsigned char KeySta[4][4] = { //全部矩阵按键的当前状态
  17.     {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}
  18. };
  19. signed long beats = 0; //电机转动节拍总数
  20. void KeyDriver();

  21. void main(){
  22.     EA = 1; //使能总中断
  23.     TMOD = 0x01; //设置 T0 为模式1
  24.     TH0 = 0xFC; //为 T0 赋初值 0xFC67,定时 1 ms
  25.     TL0 = 0x67;
  26.     ET0 = 1; //使能 T0 中断
  27.     TR0 = 1; //启动 T0

  28.     while (1){
  29.         KeyDriver(); //调用按键驱动函数
  30.     }
  31. }
  32. /* 步进电机启动函数,angle-需转过的角度 */
  33. void StartMotor(signed long angle){
  34.     //在计算前关闭中断,完成后再打开,以避免中断打断计算过程而造成错误
  35.     EA = 0;
  36.     beats = (angle * 4076) / 360; //实测为4076拍转动一圈
  37.     EA = 1;
  38. }
  39. /* 步进电机停止函数 */
  40. void StopMotor(){
  41.     EA = 0;
  42.     beats = 0;
  43.     EA = 1;
  44. }
  45. /* 按键动作函数,根据键码执行相应的操作,keycode-按键键码 */
  46. void KeyAction(unsigned char keycode){
  47.     static bit dirMotor = 0; //电机转动方向
  48.     //控制电机转动 1-9 圈
  49.     if ((keycode>=0x30) && (keycode<=0x39)){
  50.         if (dirMotor == 0){
  51.             StartMotor(360*(keycode-0x30));
  52.         }else{
  53.             StartMotor(-360*(keycode-0x30));
  54.         }
  55.     }else if (keycode == 0x26){ //向上键,控制转动方向为正转
  56.         dirMotor = 0;
  57.     }else if (keycode == 0x28){ //向下键,控制转动方向为反转
  58.         dirMotor = 1;
  59.     }else if (keycode == 0x25){ //向左键,固定正转90度
  60.         StartMotor(90);
  61.     }else if (keycode == 0x27){ //向右键,固定反转90度
  62.         StartMotor(-90);
  63.     }else if (keycode == 0x1B){ //Esc 键,停止转动
  64.         StopMotor();
  65.     }
  66. }
  67. /* 按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用 */
  68. void KeyDriver(){
  69.     unsigned char i, j;
  70.     static unsigned char backup[4][4] = { //按键值备份,保存前一次的值
  71.         {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}
  72.     };

  73.     for (i=0; i<4; i++){ //循环检测4*4的矩阵按键
  74.         for (j=0; j<4; j++){
  75.             if (backup[i][j] != KeySta[i][j]){ //检测按键动作
  76.                 if (backup[i][j] != 0){ //按键按下时执行动作
  77.                     KeyAction(KeyCodeMap[i][j]); //调用按键动作函数
  78.                 }
  79.                 backup[i][j] = KeySta[i][j]; //刷新前一次的备份值
  80.             }
  81.         }
  82.     }
  83. }
  84. /* 按键扫描函数,需在定时中断中调用,推荐调用间隔 1 ms */
  85. void KeyScan(){
  86.     unsigned char i;
  87.     static unsigned char keyout = 0; //矩阵按键扫描输出索引

  88.     static unsigned char keybuf[4][4] = { //矩阵按键扫描缓冲区
  89.         {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF},
  90.         {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}
  91.     };

  92.     //将一行的4个按键值移入缓冲区
  93.     keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;
  94.     keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;
  95.     keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;
  96.     keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;
  97.     //消抖后更新按键状态
  98.     for (i=0; i<4; i++){ //每行4个按键,所以循环4次
  99.         if ((keybuf[keyout][i] & 0x0F) == 0x00){
  100.             //连续4次扫描值为0,即 4*4 ms 内都是按下状态时,可认为按键已稳定的按下
  101.             KeySta[keyout][i] = 0;
  102.         }else if ((keybuf[keyout][i] & 0x0F) == 0x0F){
  103.             //连续4次扫描值为1,即 4*4 ms 内都是弹起状态时,可认为按键已稳定的弹起
  104.             KeySta[keyout][i] = 1;
  105.         }
  106.     }
  107.     //执行下一次的扫描输出
  108.     keyout++; //输出索引递增
  109.     keyout = keyout & 0x03; //索引值加到4即归零
  110.     //根据索引,释放当前输出引脚,拉低下次的输出引脚
  111.     switch (keyout){
  112.         case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;
  113.         case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;
  114.         case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;
  115.         case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;
  116.         default: break;
  117.     }
  118. }
  119. /* 电机转动控制函数 */
  120. void TurnMotor(){
  121.     unsigned char tmp; //临时变量
  122.     static unsigned char index = 0; //节拍输出索引
  123.     unsigned char code BeatCode[8] = { //步进电机节拍对应的 IO 控制代码
  124.         0xE, 0xC, 0xD, 0x9, 0xB, 0x3, 0x7, 0x6
  125.     };

  126.     if (beats != 0){ //节拍数不为0则产生一个驱动节拍
  127.         if (beats > 0){ //节拍数大于0时正转
  128.             index++; //正转时节拍输出索引递增
  129.             index = index & 0x07; //用&操作实现到8归零
  130.             beats--; //正转时节拍计数递减
  131.             }else{ //节拍数小于0时反转
  132.             index--; //反转时节拍输出索引递减
  133.             index = index & 0x07; //用&操作同样可以实现到-1时归7
  134.             beats++; //反转时节拍计数递增
  135.         }
  136.         tmp = P1; //用 tmp 把 P1 口当前值暂存
  137.         tmp = tmp & 0xF0; //用&操作清零低4位
  138.         tmp = tmp | BeatCode[index]; //用|操作把节拍代码写到低4位
  139.         P1 = tmp; //把低4位的节拍代码和高4位的原值送回 P1
  140.     }else{ //节拍数为0则关闭电机所有的相
  141.         P1 = P1 | 0x0F;
  142.     }
  143. }
  144. /* T0 中断服务函数,用于按键扫描与电机转动控制 */
  145. void InterruptTimer0() interrupt 1{
  146.     static bit div = 0;
  147.     TH0 = 0xFC; //重新加载初值
  148.     TL0 = 0x67;
  149.     KeyScan(); //执行按键扫描
  150.     //用一个静态 bit 变量实现二分频,即 2 ms 定时,用于控制电机
  151.     div = ~div;
  152.     if (div == 1){
  153.         TurnMotor();
  154.     }
  155. }


您需要登录后才可以回帖 登录 | 注册

本版积分规则

420

主题

10981

帖子

7

粉丝
快速回复 在线客服 返回列表 返回顶部