Combining the key input program, we designed a functional program: Pressing the number keys 1 to 9 controls the motor to rotate 1 to 9 circles; using the up and down keys changes the rotation direction, pressing the up key rotates forward 1 to 9 circles, while the down key rotates backward 1 to 9 circles; the left key fixes a forward rotation of 90 degrees, and the right key fixes a backward rotation of 90 degrees; the Esc key terminates the rotation. Through this program, we can further understand how to use keys to control the program to perform complex functions, as well as how to coordinate the control and execution modules, and your programming skills can be exercised and improved through such practical exercises.
#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] = { // Mapping table from matrix key numbers to standard keyboard key codes
{ 0x31, 0x32, 0x33, 0x26 }, // Number key 1, number key 2, number key 3, up key
{ 0x34, 0x35, 0x36, 0x25 }, // Number key 4, number key 5, number key 6, left key
{ 0x37, 0x38, 0x39, 0x28 }, // Number key 7, number key 8, number key 9, down key
{ 0x30, 0x1B, 0x0D, 0x27 } // Number key 0, ESC key, Enter key, right key
};
unsigned char KeySta[4][4] = { // Current state of all matrix keys
{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}
};
signed long beats = 0; // Total number of motor rotation beats
void KeyDriver();
void main(){
EA = 1; // Enable global interrupt
TMOD = 0x01; // Set T0 to mode 1
TH0 = 0xFC; // Assign initial value of 0xFC67 to T0, timing 1ms
TL0 = 0x67;
ET0 = 1; // Enable T0 interrupt
TR0 = 1; // Start T0
while (1){
KeyDriver(); // Call the key driver function
}
}
/* Stepper motor start function, angle – angle to be rotated */
void StartMotor(signed long angle){
// Disable interrupts before calculation, and re-enable after to avoid interrupts disrupting the calculation process
EA = 0;
beats = (angle * 4076) / 360; // Measured as 4076 beats for one full rotation
EA = 1;
}
/* Stepper motor stop function */
void StopMotor(){
EA = 0;
beats = 0;
EA = 1;
}
/* Key action function, executes corresponding operation based on key code, keycode – key code */
void KeyAction(unsigned char keycode){
static bit dirMotor = 0; // Motor rotation direction
// Control motor to rotate 1-9 circles
if ((keycode>=0x30) && (keycode<=0x39)){
if (dirMotor == 0){
StartMotor(360*(keycode-0x30));
}else{
StartMotor(-360*(keycode-0x30));
}
}else if (keycode == 0x26){ // Up key, set rotation direction to forward
dirMotor = 0;
}else if (keycode == 0x28){ // Down key, set rotation direction to backward
dirMotor = 1;
}else if (keycode == 0x25){ // Left key, fix forward rotation of 90 degrees
StartMotor(90);
}else if (keycode == 0x27){ // Right key, fix backward rotation of 90 degrees
StartMotor(-90);
}else if (keycode == 0x1B){ // Esc key, stop rotation
StopMotor();
}
}
/* Key driver function, detects key actions, dispatches corresponding action functions, needs to be called in the main loop */
void KeyDriver(){
unsigned char i, j;
static unsigned char backup[4][4] = { // Key value backup, save the previous values
{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}
};
for (i=0; i<4; i++){ // Loop to detect the 4*4 matrix keys
for (j=0; j<4; j++){
if (backup[i][j] != KeySta[i][j]){ // Detect key actions
if (backup[i][j] != 0){ // Execute action when key is pressed
KeyAction(KeyCodeMap[i][j]); // Call the key action function
}
backup[i][j] = KeySta[i][j]; // Refresh the previous backup value
}
}
}
/* Key scanning function, needs to be called in the timer interrupt, recommended call interval 1ms */
void KeyScan(){
unsigned char i;
static unsigned char keyout = 0; // Matrix key scanning output index
static unsigned char keybuf[4][4] = { // Matrix key scanning buffer
{0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}
};
// Move the values of 4 keys in one row into the buffer
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;
// Update key state after debouncing
for (i=0; i<4; i++){ // Loop 4 times for each row of 4 keys
if ((keybuf[keyout][i] & 0x0F) == 0x00){
// If the scanning value is 0 for 4 consecutive times, it can be considered that the key is stably pressed
KeySta[keyout][i] = 0;
}else if ((keybuf[keyout][i] & 0x0F) == 0x0F){
// If the scanning value is 1 for 4 consecutive times, it can be considered that the key is stably released
KeySta[keyout][i] = 1;
}
}
// Execute the next scan output
keyout++; // Increment output index
keyout = keyout & 0x03; // Reset index to 0 after reaching 4
// Release the current output pin based on the index, pull down the next output pin
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;
}
}
/* Motor control function */
void TurnMotor(){
unsigned char tmp; // Temporary variable
static unsigned char index = 0; // Beat output index
unsigned char code BeatCode[8] = { // IO control codes corresponding to stepper motor beats
0xE, 0xC, 0xD, 0x9, 0xB, 0x3, 0x7, 0x6
};
if (beats != 0){ // If the number of beats is not 0, generate a driving beat
if (beats > 0){ // If the number of beats is greater than 0, rotate forward
index++; // Increment output index when rotating forward
index = index & 0x07; // Reset index to 0 after reaching 8
beats–; // Decrement beat count when rotating forward
}else{ // If the number of beats is less than 0, rotate backward
index–; // Decrement output index when rotating backward
index = index & 0x07; // Reset index to 7 when reaching -1
beats++; // Increment beat count when rotating backward
}
tmp = P1; // Temporarily store the current value of P1
tmp = tmp & 0xF0; // Clear the lower 4 bits
tmp = tmp | BeatCode[index]; // Write the beat code to the lower 4 bits
P1 = tmp; // Send the beat code and the original value back to P1
}else{ // If the number of beats is 0, turn off all phases of the motor
P1 = P1 | 0x0F;
}
}
/* T0 interrupt service function, for key scanning and motor control */
void InterruptTimer0() interrupt 1{
static bit p = 0;
TH0 = 0xFC; // Reload initial value
TL0 = 0x67;
KeyScan(); // Perform key scanning
// Use a static bit variable to implement a divide-by-two frequency, i.e., 2ms timing, for motor control
p = ~p;
if (p == 1){
TurnMotor();
}
}This program is a comprehensive integration of the knowledge from Chapter 8 and this chapter – controlling the stepper motor rotation with keys. There are several points worth noting in this program, which we will describe as follows:
For the motor to complete the two different operations of forward and backward rotation, we did not use two functions, one for forward start and another for backward start, nor did we add a formal parameter to indicate the direction when defining the start function. The start function void StartMotor(signed long angle) only differs from the unidirectional forward start function in that the type of the formal parameter angle is changed from unsigned long to signed long. We use the inherent positive and negative characteristics of signed numbers to distinguish between forward and backward rotation, where a positive number indicates forward rotation of angle degrees, and a negative number indicates backward rotation of angle degrees. Isn’t this approach simple and clear? And doesn’t it give you a better understanding of the differences in usage between signed and unsigned numbers?
For the operation to terminate the motor rotation, we defined a separate StopMotor function to complete this, even though this function is very simple and is only called within the Esc key branch, we still separated it as a function. This practice is based on a programming principle: to use separate functions to complete specific operations of hardware whenever possible. When a hardware component contains multiple operations, organize these operational functions together to form a unified interface for the upper layer. This hierarchical processing makes the entire program clear, facilitating debugging and maintenance, as well as aiding in functionality expansion.
In the interrupt function, we need to handle both key scanning and motor driving tasks, and to avoid making the interrupt function overly complex, we have separated the key scanning and motor driving into two functions (this also aligns with the aforementioned programming principle). As a result, the logic of the interrupt function becomes simple and clear. There is also a contradiction here, as we chose a timing interval of 1ms for key scanning, while the previous examples in this chapter had a duration of 2ms for motor beats; clearly, using a 1ms timing can yield a 2ms interval, while using a 2ms timing cannot accurately yield a 1ms interval. Therefore, our approach is to keep the timer timing at 1ms, and then use a bit variable as a flag, changing its value every 1ms, and we only execute an action when its value is 1, resulting in a 2ms interval. If I wanted 3ms, 4ms, etc., I would change the bit to char or int type, increment them, and determine which value should reset to zero. This is how to achieve accurate software timing based on a hardware timer. Similar operations were also used when discussing digital tubes, so think back on that.
To help everyone learn better, Changxue Electronics has specially added a public account for microcontrollers and EDA, pushing relevant knowledge daily, hoping to assist your learning!
Changxue Microcontroller WeChat ID: changxuemcu
Centered around microcontrollers, we will learn key points, tips, and tricks related to microcontrollers together. Join us to learn!
Changxue EDA WeChat ID: changxueeda
Follow us for instant sharing of the latest EDA technology information and knowledge points, tips, and tricks related to EDA, making it easy to learn EDA technology every day.