인터럽트를 사용하여 모터의 회전을 제어하는 프로그램을 완성했지만 사실 이 프로그램은 회전을 원할 때마다 전원을 켜고 끌 수는 없습니다. 또한 앞으로 회전할 수 있을 뿐만 아니라 역회전도 할 수 있어야 합니다. 즉, 회전할 수 있을 뿐만 아니라 뒤로 회전할 수도 있어야 합니다. 자, 8장의 키 프로그램과 결합하여 다음과 같은 기능적 프로그램을 설계해 보겠습니다. 숫자 키 1~9를 눌러 모터를 제어하고 위쪽 및 아래쪽 키를 사용합니다. 회전 방향을 변경하고, 위쪽 버튼을 누르면 앞으로 1~9회 회전하고, 아래쪽 버튼을 누르면 반대 방향으로 1~9회 회전하고, 오른쪽 버튼을 누르면 90도 회전합니다. 각도, Esc 키를 누르면 전환이 종료됩니다. 이 프로그램을 통해 우리는 복잡한 기능을 완성하기 위해 버튼을 사용하여 프로그램을 제어하는 방법, 제어 모듈과 실행 모듈 간의 작업을 조정하는 방법을 더 깊이 이해할 수 있으며 이러한 이론적 연습을 통해 프로그래밍 수준도 연습하고 향상시킬 수 있습니다.
#include <reg52.h> 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<=0x39)){ if (dirMotor == 0){ StartMotor(360*(keycode-0x30)); }else{ StartMotor(-360*(keycode-0x30)); } }else if (keycode == 0x26){ //向上键,掌握迁移转变偏向为正转 dirMotor = 0; }else if (keycode == 0x28){ //向下键,掌握迁移转变偏向为反转 dirMotor = 1; }else if (keycode == 0x25){ //向左键,固定正转 90 度 StartMotor(90); }else if (keycode == 0x27){ //向右键,固定反转 90 度 StartMotor(-90); }else if (keycode == 0x1B){ //Esc 键,中止迁移转变 StopMotor(); } } /* 按键驱动函数,检测按键举措,调剂响应举措函数,需在主轮回中挪用 */ void KeyDriver(){ unsigned char i, j; static unsigned char backup[4][4] = { //按键值备份,保管前一次的值 {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1} }; for (i=0; i<4; i++){ //轮回检测 4*4 的矩阵按键 for (j=0; j<4; j++){ if (backup[i][j] != KeySta[i][j]){ //检测按键举措 if (backup[i][j] != 0){ //按键按下时履行举措 KeyAction(KeyCodeMap[i][j]); //挪用按键举措函数 } backup[i][j] = KeySta[i][j]; //刷新前一次的备份值 } } } } /* 按键扫描函数,需在准时中缀中挪用,引荐挪用距离 1ms */ void KeyScan(){ unsigned char i; static unsigned char keyout = 0; //矩阵按键扫描输入索引 static unsigned char keybuf[4][4] = { //矩阵按键扫描缓冲区 {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF} }; //将一行的 4 个按键值移入缓冲区 keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1; keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2; keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3; keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4; //消抖后更新按键形态 for (i=0; i<4; i++){ //每行 4 个按键,所以轮回 4 次 if ((keybuf[keyout][i] & 0x0F) == 0x00){ //延续 4 次扫描值为 0,即 4*4ms 内多是按下形态时,可以为按键已波动的按下 KeySta[keyout][i] = 0; }else if ((keybuf[keyout][i] & 0x0F) == 0x0F){ //延续 4 次扫描值为 1,即 4*4ms 内多是弹起形态时,可以为按键已波动的弹起 KeySta[keyout][i] = 1; } } //履行下一次的扫描输入 keyout++; //输入索引递增 keyout = keyout & 0x03; //索引值加到 4 即归零 //依据索引,释放以后输入引脚,拉低下次的输入引脚 switch (keyout){ case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break; case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break; case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break; case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break; default: break; } } /* 电机迁移转变掌握函数 */ void TurnMotor(){ unsigned char tmp; //暂时变量 static unsigned char index = 0; //节奏输入索引 unsigned char code BeatCode[8] = { //步进电机节奏对应的 IO 掌握代码 0xE, 0xC, 0xD, 0x9, 0xB, 0x3, 0x7, 0x6 }; if (beats != 0){ //节奏数不为 0 则发生一个驱动节奏 if (beats > 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 div = 0; TH0 = 0xFC; //从新加载初值 TL0 = 0x67; KeyScan(); //履行按键扫描 //用一个静态 bit 变量完成二分频,即 2ms 准时,用于掌握电机 div = ~div; if (div == 1){ TurnMotor(); } }
이 프로그램은 8장과 이 장의 지식을 종합한 것입니다. 즉, 버튼을 사용하여 스테퍼 모터 전환을 제어하는 것입니다. 프로그램에는 주목할 만한 몇 가지 사항이 있습니다.
모터는 정회전과 역회전이라는 두 가지 다른 작업을 완료해야 한다는 점을 고려하여 두 가지 기능을 사용하지 않습니다. 정회전 시작 기능 및 역회전 시작 기능 이를 달성하기 위해 시작 기능을 정의할 때 방향을 나타내는 메소드 매개변수가 추가되지 않습니다. 시작 함수 void StartMotor(부호 있는 긴 각도)와 단방향 정방향 회전을 위한 시작 함수 사이의 유일한 차이점은 메소드 매개변수 각도의 유형이 고유한 양수 및 음수 특성을 사용한다는 것입니다. 정회전과 역회전을 구별하기 위해 음수는 정회전 각도를 나타내고 양수는 역회전 각도를 나타냅니다. 이 해법은 매우 간단하고 명확하지 않습니까? 부호 있는 숫자와 부호 없는 숫자의 다양한 사용법을 더 잘 이해하고 있나요?
모터 회전 작업을 종료하기 위해 별도의 StopMotor 함수를 정의하여 완료했습니다. 비록 이 함수가 매우 복잡하고 Esc 키 분기에서만 호출되지만 여전히 별도로 언급합니다. 함수로 나옵니다. 이 접근 방식은 하드웨어의 특정 작업을 완료하기 위해 별도의 기능을 사용하려고 시도하는 프로그래밍 원칙을 기반으로 합니다. 하드웨어에 여러 작업이 포함된 경우 이러한 작업 기능을 함께 구성하여 기본 계층에 대한 통합 인터페이스를 형성합니다. 이러한 계층적 처리는 전체 프로그램을 명확하게 계층적으로 만들어 프로그램의 디버깅, 유지 관리 및 기능 확장에 도움이 됩니다.
인터럽트 기능은 키 스캐닝과 모터 구동이라는 두 가지 작업을 처리해야 합니다. 인터럽트 기능이 너무 복잡해지는 것을 방지하기 위해 키 스캐닝과 모터 구동이라는 두 가지 기능을 분리했습니다. (이 역시 위의 내용과 일치합니다. 2. 프로그래밍 원리), 중위 함수의 논리가 간결하고 명확해집니다. 여기에는 또 다른 모순이 있습니다. 즉, 버튼 스캔을 위해 선택한 타이밍 시간은 1ms인 반면, 이 장 앞의 예에서는 모터 리듬 지속 시간이 분명히 2ms일 수 있으며, 1ms의 타이밍을 사용하면 2ms의 거리를 결정할 수 있습니다. 그러나 2ms의 타이밍을 사용하면 2ms의 거리를 결정할 수 있습니다. 그러나 우리는 정확한 1ms 거리를 얻을 수 없으므로 타이머는 여전히 1ms의 시간을 유지하고 이를 비트 변수로 표시하여 값을 변경합니다. 1ms마다, 값이 1일 때만 작업을 실행하도록 선택합니다. 이는 2ms의 거리입니다. 3ms 또는 4ms를 원하면 비트를 char 또는 int 유형으로 변경한 다음 증가시키고 어떤 값을 사용해야 하는지 결정합니다. 0으로 재설정합니다. 이는 하드웨어 타이머를 기반으로 정확한 소프트웨어 타이밍을 달성하기 위한 것입니다.
위 내용은 28BYJ-48 스테퍼 모터의 마스터링 절차는 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!