根据 Arduino门禁人机接口调试 Version 1.0 基于Groove Beginner的背板的设计基础上,按照 从0 开始 DIY你的Arduino UNO 设计方法,将ATmega328直接与接口电路设计成一个单一的电路板,便可以大大简化系统的构成了。
▲ 图1-1 最小系统原理图
▲ 图1-2 ESP8266子系统电路
▲ 图1-3 接口电路
▲ 图1-4 USB下载电路接口
根据下图所示的Arduino管脚的定义,结合前面的原理图设计,可以分别确定按键
,LED模块
,舵机
,语音模块
,人脸模块
,ESP8266模块的接口
。
▲ 图2-1 Arduino 管脚定义
按键的行扫描与列扫描分别使用 D4
,D5
,D6
,D7
,D8
,D9
,D18
,D19
。键盘的读取参考程序参见:
/* **============================================================================== ** TESTKEY.C: -- by Dr. ZhuoQing, 2021-05-25 ** **============================================================================== */ //------------------------------------------------------------------------------ #define KEYLINE_1 4 #define KEYLINE_2 5 #define KEYLINE_3 6 #define KEYLINE_4 7 #define KEYCODE_1 8 #define KEYCODE_2 9 #define KEYCODE_3 18 #define KEYCODE_4 19 #define KEY_NULL 0xff #define KEY_1 0x47 #define KEY_2 0x4B #define KEY_3 0x4D #define KEY_4 0x37 #define KEY_5 0x3B #define KEY_6 0x3D #define KEY_7 0x27 #define KEY_8 0x2B #define KEY_9 0x2D #define KEY_0 0x17 #define KEY_A 0x4E #define KEY_B 0x3E #define KEY_C 0x2E #define KEY_D 0x1E #define KEY_E 0x1D #define KEY_F 0x1B void keyLineSet(unsigned char ucLine) { if(ucLine & 0x1) digitalWrite(KEYLINE_1, HIGH); else digitalWrite(KEYLINE_1, LOW); if(ucLine & 0x2) digitalWrite(KEYLINE_2, HIGH); else digitalWrite(KEYLINE_2, LOW); if(ucLine & 0x4) digitalWrite(KEYLINE_3, HIGH); else digitalWrite(KEYLINE_3, LOW); if(ucLine & 0x8) digitalWrite(KEYLINE_4, HIGH); else digitalWrite(KEYLINE_4, LOW); } void keySetup(void) { pinMode(KEYLINE_1, OUTPUT); pinMode(KEYLINE_2, OUTPUT); pinMode(KEYLINE_3, OUTPUT); pinMode(KEYLINE_4, OUTPUT); pinMode(KEYCODE_1, INPUT_PULLUP); pinMode(KEYCODE_2, INPUT_PULLUP); pinMode(KEYCODE_3, INPUT_PULLUP); pinMode(KEYCODE_4, INPUT_PULLUP); keyLineSet(0x0); } unsigned char keyReadCode(void) { keyLineSet(0xe); if(keyCode() != 0xf) return 0x10 | keyCode(); keyLineSet(0xd); if(keyCode() != 0xf) return 0x20 | keyCode(); keyLineSet(0xb); if(keyCode() != 0xf) return 0x30 | keyCode(); keyLineSet(0x7); if(keyCode() != 0xf) return 0x40 | keyCode(); return 0xff; } unsigned char keyCode(void) { unsigned char ucCode; ucCode = 0x0; if(digitalRead(KEYCODE_1) == HIGH) ucCode |= 0x1; if(digitalRead(KEYCODE_2) == HIGH) ucCode |= 0x2; if(digitalRead(KEYCODE_3) == HIGH) ucCode |= 0x4; if(digitalRead(KEYCODE_4) == HIGH) ucCode |= 0x8; return ucCode; } //------------------------------------------------------------------------------ unsigned char Hex2Text(unsigned char ucCode) { if(ucCode < 10) { return '0' + ucCode; } return 'A' + ucCode - 10; } void SendHEX8(unsigned char ucCode) { Serial.write(Hex2Text(ucCode >> 4)); Serial.write(Hex2Text(ucCode & 0xf)); } //------------------------------------------------------------------------------ void setup(void) { Serial.begin(115200); keySetup(); } //------------------------------------------------------------------------------ void loop(void) { delay(250); SendHEX8(keyReadCode()); Serial.write("\r\n"); } //============================================================================== // END OF THE FILE : TESTKEY.C //------------------------------------------------------------------------------
控制点阵LED模块的四个IO口分别为 D14
,D15
,D16
,D17
。
对于LED模块控制参考程序为:
/* **============================================================================== ** TESTLED.C: -- by Dr. ZhuoQing, 2021-05-25 ** **============================================================================== */ #define LED_DATA1 14 #define LED_DATA2 15 #define LED_CLK1 16 #define LED_CLK2 17 //------------------------------------------------------------------------------ unsigned char g_ucLEDBuffer[4][16]; void ledSetup(void) { int i, j; for(i = 0; i < 4; i ++) { for(j = 0; j < 16; j ++) { g_ucLEDBuffer[i][j] = 0x0; } } pinMode(LED_DATA1, OUTPUT); pinMode(LED_DATA2, OUTPUT); pinMode(LED_CLK1, OUTPUT); pinMode(LED_CLK2, OUTPUT); digitalWrite(LED_CLK1, HIGH); digitalWrite(LED_CLK2, HIGH); digitalWrite(LED_DATA1, HIGH); digitalWrite(LED_DATA2, HIGH); } void ledSetData12(unsigned char ucData12) { if(ucData12 & 0x1) digitalWrite(LED_DATA1, HIGH); else digitalWrite(LED_DATA1, LOW); if(ucData12 & 0x2) digitalWrite(LED_DATA2, HIGH); else digitalWrite(LED_DATA2, LOW); } void ledClock1(void) { digitalWrite(LED_CLK1, HIGH); // clock up 7us digitalWrite(LED_CLK1, LOW); } void ledClock2(void) { digitalWrite(LED_CLK2, HIGH); digitalWrite(LED_CLK2, LOW); } void ledStart(void) { digitalWrite(LED_DATA1, LOW); digitalWrite(LED_DATA2, LOW); digitalWrite(LED_CLK1, LOW); digitalWrite(LED_CLK2, LOW); } void ledStop(void) { digitalWrite(LED_DATA1, LOW); digitalWrite(LED_DATA2, LOW); digitalWrite(LED_CLK1, HIGH); digitalWrite(LED_CLK2, HIGH); digitalWrite(LED_DATA1, HIGH); digitalWrite(LED_DATA2, HIGH); } void ledWriteData(unsigned char ucChar1, unsigned char ucChar2, unsigned char ucChar3, unsigned char ucChar4) { unsigned char i; unsigned char ucMask; ucMask = 0x1; for(i = 0; i < 8; i++) { if(ucChar1 & ucMask) digitalWrite(LED_DATA1, HIGH); else digitalWrite(LED_DATA1, LOW); if(ucChar2 & ucMask) digitalWrite(LED_DATA2, HIGH); else digitalWrite(LED_DATA2, LOW); digitalWrite(LED_CLK1, HIGH); // clock up 7us digitalWrite(LED_CLK1, LOW); if(ucChar3 & ucMask) digitalWrite(LED_DATA1, HIGH); else digitalWrite(LED_DATA1, LOW); if(ucChar4 & ucMask) digitalWrite(LED_DATA2, HIGH); else digitalWrite(LED_DATA2, LOW); digitalWrite(LED_CLK2, HIGH); // clock up 7us digitalWrite(LED_CLK2, LOW); ucMask = ucMask << 1; } } //------------------------------------------------------------------------------ #define LEDCMD_ADD_INC 0x40 #define LEDCMD_ADD_SET 0x44 #define LEDCMD_MODE_CLOSE 0x80 #define LEDCMD_MODE_OPEN 0x8a //------------------------------------------------------------ void ledWriteByteAll(unsigned char ucChar) { ledWriteData(ucChar, ucChar, ucChar, ucChar); } void ledWriteData16All(unsigned char ucData) { unsigned char i; ledStart(); ledWriteByteAll(LEDCMD_ADD_INC); ledStop(); ledStart(); ledWriteByteAll(0xc0); for(i = 0; i < 16; i ++) { ledWriteByteAll(ucData); } ledStop(); ledStart(); ledWriteByteAll(LEDCMD_MODE_OPEN); ledStop(); } //------------------------------------------------------------------------------ void ledWriteBuffer(void) { unsigned char i; ledStart(); ledWriteByteAll(LEDCMD_ADD_INC); ledStop(); ledStart(); ledWriteByteAll(0xc0); for(i = 0; i < 16; i ++) { ledWriteData(g_ucLEDBuffer[0][i], g_ucLEDBuffer[1][i], g_ucLEDBuffer[2][i], g_ucLEDBuffer[3][i]); } ledStop(); ledStart(); ledWriteByteAll(LEDCMD_MODE_OPEN); ledStop(); } //------------------------------------------------------------------------------ #define LED_PIN 13 void setup(void) { ledSetup(); pinMode(LED_PIN, OUTPUT); } //------------------------------------------------------------------------------ unsigned char ucCount = 0; void loop(void) { int i, j; ucCount ++; if(ucCount & 0x1) digitalWrite(LED_PIN, HIGH); else digitalWrite(LED_PIN, LOW); for(i = 0; i < 4; i ++) { for(j = 0; j < 16; j ++) { g_ucLEDBuffer[i][j] = ucCount; } } ledWriteBuffer(); delay(500); } //============================================================================== // END OF THE FILE : TESTLED.C //------------------------------------------------------------------------------
将按键与LED模块符合在一起的测试程序,请参见 Arduino门禁人机接口调试 Version 1.0 最后的例程。
使用ATmega328 的MOSI(PB3)接口,对应的Arduino接口 D11。
根据 Grove Beginner Kits基础实验 Arduino 对于IO口控制舵机的讨论,需要将定时器2的寄存器TCCR2B进行修改,才能够将输出的频率修改成122.5Hz,满足舵机控制所需。参考处贴上如下:
const int PWM_PIN = 11; void setup() { TCCR2B = TCCR2B & B11111000 | B00000110; pinMode(PWM_PIN, OUTPUT); } void loop() { delay(1000); analogWrite(PWM_PIN, 63); delay(1000); analogWrite(PWM_PIN, 31); }
上述程序分别输出31(对应1ms)与63(对应2ms),可以控制舵机从最左边移动到最右边。
PWM脉宽 | PWM设置值 | 实际输出时间(us) | 实测时间(us) |
---|---|---|---|
1ms | 31 | 0.992ms | 0.988 |
1.5ms | 47 | 1.504ms | 1.5ms |
2ms | 63 | 2.016ms | 2.01ms |
/* **============================================================================== ** TESTPWM.C: -- by Dr. ZhuoQing, 2021-05-30 ** **============================================================================== */ #define ON(pin) digitalWrite(pin, HIGH) #define OFF(pin) digitalWrite(pin, LOW) #define VAL(pin) digitalRead(pin) #define IN(pin) pinMode(pin, INPUT) #define OUT(pin) pinMode(pin, OUTPUT) const int LED_PIN = 13; const int PWM_PIN = 11; const int SERVO_MID = 47; const int SERVO_LEFT = 31; const int SERVO_RIGHT = 63; void servoSetup(void) { TCCR2B = TCCR2B & B11111000 | B00000110; pinMode(PWM_PIN, OUTPUT); analogWrite(PWM_PIN, SERVO_MID); } //------------------------------------------------------------------------------ void setup(void) { pinMode(LED_PIN, OUTPUT); servoSetup(); } //------------------------------------------------------------------------------ void loop(void) { analogWrite(PWM_PIN, SERVO_LEFT); delay(1000); analogWrite(PWM_PIN, SERVO_RIGHT); delay(1000); } //============================================================================== // END OF FILE : TESTPWM.C //------------------------------------------------------------------------------
语音模块使用了SoftwareSerial。利用ATmega328 的 MISO,SCK两个管脚(分别对应)D12,D13作为RX,TX。
语音模块使用9600的波特率。通过软件串口发送对应文字的编码便可以发出声音。
#include <SoftwareSerial.h> //------------------------------------------------------------------------------ SoftwareSerial mySerial(12, 13); void setup() { mySerial.begin(9600); } //------------------------------------------------------------------------------ void loop() { mySerial.write('U'); delay(10); }
下面的程序发送“卓一康”的读音。
/* **============================================================================== ** TESTSERIAL.C: -- by Dr. ZhuoQing, 2021-05-30 ** **============================================================================== */ #include <SoftwareSerial.h> //------------------------------------------------------------------------------ SoftwareSerial ttsPort(12, 13); void setup(void) { ttsPort.begin(9600); } //------------------------------------------------------------------------------ void loop(void) { ttsPort.write("\xd7\xbf\xd2\xbb\xbf\xb5"); delay(1000); } //============================================================================== // END OF FILE : TESTSERIAL.C //------------------------------------------------------------------------------
ESP8266有ATmega328 的SoftwareSerial相连接。使用到D2,D3两个端口。通讯的波特率为115200。
下面的示例程序显示了发送与接收的过程。
/* **============================================================================== ** UNORECEIVE.C: -- by Dr. ZhuoQing, 2021-05-30 ** **============================================================================== */ #include <SoftwareSerial.h> #define ON(pin) digitalWrite(pin, HIGH) #define OFF(pin) digitalWrite(pin, LOW) #define VAL(pin) digitalRead(pin) #define IN(pin) pinMode(pin, INPUT) #define OUT(pin) pinMode(pin, OUTPUT) SoftwareSerial ttsPort(12, 13); SoftwareSerial wifiPort(2, 3); //------------------------------------------------------------------------------ void setup(void) { char c; ttsPort.begin(9600); wifiPort.begin(115200); wifiPort.listen(); wifiPort.setTimeout(500); Serial.begin(115200); //---------------------------------------------------------------------- } //------------------------------------------------------------------------------ int count = 0; String str; void loop(void) { char c; char buf[256]; ttsPort.listen(); wifiPort.print("http://192.168.4.2:8000/lock/update/?type=LOCK&detail=AAAA\r"); wifiPort.listen(); while(1) { str = wifiPort.readString(); Serial.print(str); if(wifiPort.available() == 0) break; } delay(2000); } //============================================================================== // END OF FILE : UNORECEIVE.C //------------------------------------------------------------------------------
■ 相关文献链接:
● 相关图表链接: