Arduino学习笔记A11 – Arduino模拟电脑键盘(基于AVR-USB的USB-HID设备) [转]

Arduino模拟电脑键盘(基于AVR-USB的USB-HID设备)

关于此帖子的其他讨论,还可以看看
http://geek-workshop.com/thread-2303-1-1.html
http://geek-workshop.com/thread-2310-1-1.html

键盘作为经典的输入设备,使用在很多互动中都有特别的优势,比如我们可以通过键盘直接给flash传递按键事件。而无需通过串口之类的特殊接口,虽然我们可以拆一个传统的键盘,然后将里面的按键引出来,但是这样有一个缺点,就是键值不能动态改变并且不能一次多键。使用模拟键盘的话,我们就可以随意在程序设置按键的时间和键值。比如本文的例子就是按下一个按键,模拟键盘就在电脑输入“HELLO WORLD”。

硬件部分:
材料清单:
Arduino x1
68Ω电阻 x2 (没有68Ω的话,用50~100Ω电阻也行)
2.2kΩ电阻 x1 (没有2.2kΩ的话,用1.5k~2.2k电阻也行)
USB连接线(一端是USB口,另一端是电线) x1
3.6v 稳压管(建议选用功耗0.25~0.5w的)x2
194146uld4tdhd1b111w9d1942021gy6n1v41yne26ia

电路原理图:
120616mer6qi8i8smt5qs6
电路图解说:
1、两个68Ω的电阻起到限流和保护作用,防止在意外情况下损坏计算机的USB端口或单片机的端口。
2、2.2kΩ电阻是上拉电阻,用于分辨总线状态。如果上拉电阻接于D+和+5v端则是高速USB设备,接于D-与+5v端,则是低速设备。此处键盘传输速率不高,接到D-作为低速设备即可。
3、D+和D-上的3.6V稳压二极管D1和D2起到限制数据线上的电平的作用。因为在USB规范中规定数据线D+和D-上的电平范围是3.0V至3.6V,而AVR单片机的输出电平是Vcc。如果单片机的Vcc是5V,在没有D1和D2的情况下将造成电平不匹配,会造成在很多计算机中无法正确识别出USB设备。如果用户系统的Vcc在3.0V至3.6V之间,就可以省略这两个稳压二极管。从这里也可以看出用户系统的Vcc必须高于3V。
4、由于低速AVRUSB所需要的是1.5MHz时钟,而单片机每8条指令就能精确完成一个数据位的采集。所以AVRUSB最小单片机时钟频率是12MHz。并且可以使用的时钟频率有12MHz、12.8MHz、15MHz、16MHz、16.5MHz、20MHz,其他的不支持。所以如果使用最小系统制作此模拟键盘的话8MHz的话,ATMega8L不能用。
————————
软件部分:

Arduino支持库文件:
http://soft1.wmzhe.com/download/ … ino/UsbKeyboard.zip
UsbKeyboard.zip (112.45 KB, 下载次数: 1421)
文件下载后解压到arduino编译器的libraries文件夹下面。

注意:

1、UsbKeyboard库中,usbconfig.h里面可以更改USB接线的引脚定义,下面给出一个大概解释(下面的PORTD是指AVR单片机的PORTD,要查询Arduino原理图才能得到是Arduino的哪个引脚):

#define USB_CFG_IOPORTNAME D
USB输入输出引脚使用AVR单片机的PORTD,如果改成B就是使用PORTB
#define USB_CFG_DMINUS_BIT 4
USB的D-接PORTD的第四位PD4,对应Arduino D4
#define USB_CFG_DPLUS_BIT  2
USB的D+接PORTD的第二位PD2,对应Arduino D2
#define USB_CFG_PULLUP_IOPORTNAME D
USB上拉引脚使用AVR单片机的PORTD,如果改成B就是使用PORTB
#define USB_CFG_PULLUP_BIT  5
USB的上拉电阻接PORTD的第五位PD5,对应Arduino  D5

2、在UsbKeyboard库的UsbKeyboard.h里面,有关于模拟键值的表
#define KEY_A       4
#define KEY_B       5
#define KEY_C       6
#define KEY_D       7
#define KEY_E       8
等。但不齐全。经过测试,其实这个键盘基本可以模拟几乎所有键值(Power,Sleep,Pause似乎不能)。
比如方向键右左下上分别对应79,80,81,82数字。即写成
UsbKeyboard.sendKeyStroke(79);
UsbKeyboard.sendKeyStroke(81);
等。由于整理比较麻烦,大家可以自己下载个KeyboardTest软件测试不同数字下面的键值。

程序示例:
下面的例子演示了用Arduino虚拟键盘的应用例子。打开记事本,然后将Arduino的D12引脚和GND连起来,就会打印HELLO WORLD字样。

ARDUINO 代码复制打印

  1. /*
  2. Arduino模拟键盘 by Ansifa
  3. 2012.6.8
  4. 功能描述:插上此模拟键盘,打开记事本,然后按下按钮,即可在记事本打印出HELLO WORLD字样
  5. 接线方法:
  6. <img src=”http://www.geek-workshop.com/forum.php?mod=image&aid=5359&size=300×300&key=e9fa5559e6d5724d51f770bc6c55b941&nocache=yes&type=fixnone” border=”0″ aid=”attachimg_5359″ alt=””>
  7. Arduino D2接68Ω电阻后,接USB线D+
  8. Arduino D4接68Ω电阻后,接USB线D-
  9. Arduino D5接2.2kΩ电阻后,接USB线D-
  10. Arduino D2接3.6v稳压管到GND
  11. Arduino D4接3.6v稳压管到GND
  12. +5v接USB线VCC
  13. GND接USB线GND
  14. Arduino D1接一个开关到GND
  15. 附:USB线序颜色(由于各生产厂不同,不一定准确,仅供参考)
  16. *USB键鼠:      |        *USB接口
  17. 白<->VCC        |        红<->VCC
  18. 橙<->D-         |        白<->D-
  19. 绿<->D+         |        绿<->D+
  20. 蓝<->GND        |        黑<->GND
  21. */
  22. #include “UsbKeyboard.h”
  23. int KEYPIN = 1;                //按键接在D1引脚,也可以改成任何引脚
  24. void setup()
  25. {
  26.   TIMSK0 &= !(1 << TOIE0);        //
  27.   pinMode(KEYPIN, INPUT);
  28.   digitalWrite(KEYPIN, HIGH);
  29. }
  30. void loop()
  31. {
  32.   UsbKeyboard.update();
  33.   if(digitalRead(KEYPIN) == HIGH)
  34.   {
  35.     delay(100);
  36.     if(digitalRead(KEYPIN) == LOW)
  37.     {
  38.       UsbKeyboard.sendKeyStroke(KEY_H);
  39.       UsbKeyboard.sendKeyStroke(KEY_E);
  40.       UsbKeyboard.sendKeyStroke(KEY_L);
  41.       UsbKeyboard.sendKeyStroke(KEY_L);
  42.       UsbKeyboard.sendKeyStroke(KEY_O);
  43.       UsbKeyboard.sendKeyStroke(KEY_SPACE);
  44.       UsbKeyboard.sendKeyStroke(KEY_W);
  45.       UsbKeyboard.sendKeyStroke(KEY_O);
  46.       UsbKeyboard.sendKeyStroke(KEY_R);
  47.       UsbKeyboard.sendKeyStroke(KEY_L);
  48.       UsbKeyboard.sendKeyStroke(KEY_D);
  49.       UsbKeyboard.sendKeyStroke(KEY_ENTER);
  50.     }
  51.   }
  52. }

注意,先插上Arduino数据线,将程序写入Arduino。然后拔掉数据线,将模拟键盘USB线接到电脑,即可使用。

下面随便点实物图

刚焊好的正反面
194204xv880393zvisyxn32154270440zvdpvvocmaqc

插数据线写程序进Arduino:
1941595x18zxgzaan1fgk5

转自http://www.geek-workshop.com/thread-1137-1-1.html, 版权归原作者所有

Arduino读取键盘[转载]

Arduino读取键盘

Arduino_and_Keypad
这里提供两种方式从Arduino读取键盘。第一种方式是使用矩阵式(Matrix)键盘,另一种方式是使用PS2键盘。
矩阵键盘(Matrix keypad)
首先必须安装Arduino Keypad键盘库(Keypad library),Arduino Keypad键盘库可以从Arduino Playground下载。Arduino Keypad键盘库让你读取矩阵式键盘而不用编写复杂的代码,此键盘库可以读取3×4, 4×4以及各种矩阵结构的键盘。
使用Arduino Keypad键盘库注意事项
  • 该键盘库是属于无阻塞式,按下谋键不放,其余(接下来)的代码还是会继续运行
  • 如果编写控制键盘处运用到delay(),这将造成键盘反应迟顿
  • 按下谋键,getKey()只返回一个键值,而不是自动重复。松开按键时,可以追踪其RELEASED event

安装Arduino Keypad键盘库

  • 下载Arduino Keypad键盘库
  • 将下载了的文件(keypad.zip)解压至Arduino软件的libraries文件夹,如图
Arduino_keypad_library
  • 打开Arduino软件
  • 选择File>Examples>Keypad,将会看见以下画面,表示Arduino Keypad键盘库安装成功
Arduino_keypad_examples
4×4矩阵keypad示范
根据下面接线连接键盘至Arduino
4x4_matrix_membrane_keypad_pinout
Arduino
4×4 Keypad
D2
1
D3
2
D4
3
D5
4
D6
5
D7
6
D8
7
D9
8
上载以下代码至Arduino
#include <Keypad.h>

const byte ROWS = 4; // Four rows
const byte COLS = 4; // Four columns

//Define the keymap
char keys[ROWS][COLS] = {
{‘1′,’2′,’3′,’A’},
{‘4′,’5′,’6′,’B’},
{‘7′,’8′,’9′,’C’},
{‘*’,’0′,’#’,’D’}
};

//// Connect keypad ROW0, ROW1, ROW2 and ROW3 to these Arduino pins.
byte rowPins[ROWS] = {6,7,8,9};

// Connect keypad COL0, COL1, COL2 and COL3 to these Arduino pins.
byte colPins[COLS] = {2,3,4,5}; //connect to column pinouts

// Create the Keypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup(){
Serial.begin(9600);
}

void loop(){
char key = keypad.getKey();

if (key != NO_KEY){
Serial.println(key);
}
}

使用Arduino软件自带的串口监视器(Serial Monitor)来测试此程序(Baud rate必须设成9600)。当按下谋键时,其返回值将显示在串口监视器。


PS2键盘
首先必须安装Arduino PS2键盘库(PS2keyboard library),Arduino PS2键盘库可以从这里下载
安装Arduino PS2键盘库
  • 下载Arduino PS2键盘库
  • 将下载了的文件(PS2keyboard.zip)解压至Arduino软件的libraries文件夹
  • 安装方法与安装keypad相似,请参考之。
 
PS2键盘示范
根据下面接线连接键盘至Arduino
PS2_keyboarad_pinout
Keyboard
Arduino
4 (+5V)
5V
3 (GND)
GND
5 (Clock)
Digital Pin 3
1 (Datak)
Digital Pin 4

上载以下代码至Arduino

#include <PS2Keyboard.h>

const int DataPin = 8;
const int IRQpin = 5;

PS2Keyboard keyboard;

void setup() {
delay(1000);
keyboard.begin(DataPin, IRQpin);
Serial.begin(9600);
Serial.println(“Keyboard Test:”);
}

void loop() {
if (keyboard.available()) {

char c = keyboard.read();  // read the next key

// check for some of the special keys
if (c == PS2_ENTER) {
Serial.println();
} else if (c == PS2_TAB) {
Serial.print(“[Tab]”);
} else if (c == PS2_ESC) {
Serial.print(“[ESC]”);
} else if (c == PS2_PAGEDOWN) {
Serial.print(“[PgDn]”);
} else if (c == PS2_PAGEUP) {
Serial.print(“[PgUp]”);
} else if (c == PS2_LEFTARROW) {
Serial.print(“[Left]”);
} else if (c == PS2_RIGHTARROW) {
Serial.print(“[Right]”);
} else if (c == PS2_UPARROW) {
Serial.print(“[Up]”);
} else if (c == PS2_DOWNARROW) {
Serial.print(“[Down]”);
} else if (c == PS2_DELETE) {
Serial.print(“[Del]”);
} else {
Serial.print(c);  // otherwise, just print all normal characters
}
}
}

使用Arduino软件自带的串口监视器(Serial Monitor)来测试此程序(Baud rate必须设成9600)。当按下谋键时,其返回值将显示在串口监视器。


增加按键音效
按照下面图象连接PC扬声器(此PC扬声器可以从废棄的电脑主板拆岀来),然後稍微更改代码。由于没有此PC扬声器的规格说明,估计供电5V且功率非常小,能够直接由Arduino驱动。如果不放心,可以在Aruino pin10与扬声器之间添加一个100欧姆电阻器。

Connecting_PC_speaker_to_Arduino

代码方面使用了tone()函数,具体可以浏览Arduino Reference网站
http://arduino.cc/en/Reference/Tone

tone()用法

  • tone(pin, frequency)
  • tone(pin, frequency, duration)
  1. pin是连接扬声器的引脚
  2. frequency是输出频率,频率越低,音频就越低。
  3. duration音频输出持续时间
  1. #include <Keypad.h>
  2. const byte ROWS = 4; // Four rows
  3. const byte COLS = 4; // Four columns
  4. //Define the keymap
  5. char keys[ROWS][COLS] = {
  6. {‘1′,’2′,’3′,’A’},
  7. {‘4′,’5′,’6′,’B’},
  8. {‘7′,’8′,’9′,’C’},
  9. {‘*’,’0′,’#’,’D’}
  10. };
  11. //// Connect keypad ROW0, ROW1, ROW2 and ROW3 to these Arduino pins.
  12. byte rowPins[ROWS] = {6,7,8,9};
  13. // Connect keypad COL0, COL1, COL2 and COL3 to these Arduino pins.
  14. byte colPins[COLS] = {2,3,4,5}; //connect to column pinouts
  15. // Create the Keypad
  16. Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
  17. void setup(){
  18. Serial.begin(9600);
  19. }
  20. void loop(){
  21. char key = keypad.getKey();
  22. if (key != NO_KEY){
  23. delay(50); //act as debounce
  24. beep();
  25. Serial.println(key);
  26. }
  27. }
  28. #define SPEAKER_PIN 10
  29. void beep(){
  30. tone(SPEAKER_PIN,2000,90);
  31. delay(20);
  32. noTone(SPEAKER_PIN);
  33. }
转自
http://ediy.com.my/index.php/2012-10-21-15-15-03/2013-04-14-05-06-50/item/65-arduino%E8%AF%BB%E5%8F%96%E9%94%AE%E7%9B%98

arduino 使用多个SPI设备 how to running multiple SPI device on arduino

how to running PN532 and W5100 on same board

最近研究arduino开发板,尝试将NFC板(PN532)和网络扩展板(w5100)组合在一起,碰到个问题,这两块板子都是通过SPI总线和arduino通信,同时占用pin10,11,12,13三个口,其中10是片选信号SS。这两个设备始终无法同时工作,出现很多莫名其妙的问题。

154149rr5dubo9wbj9wlrw

比如我编译IDE自带的webserver程序,设置了ip地址为192.168.1.15,启动以后,ping 192.168.1.15可以通,但在串口接收到 ip地址为192.139.1.15,甚至是192.192.192.192,或者其他乱七八糟的地址。在PC上访问http://192.168.1.15无法打开。拔除NFC扩展板,web访问正常。然后开始漫长google之路。

参考链接

http://arduino.cc/en/Main/ArduinoEthernetShield

1.首先确认两个设备不能使用同一个SS pin,将nfc的nss针插到其他pin(貌似1,2,4,不能使用,4是SD卡的片选),我选择5,然后在arduino的setup里面增加

pinMode(5,OUTPUT);

digitalWrite(5,HIGH);

测试同时连接NFC和网络,只初始化网卡,webserver正常;如果同时初始化网卡和NFC,依旧无法访问webserver

参考链接

http://www.circuitsathome.com/mcu/running-multiple-slave-devices-on-arduino-spi-bus

2.继续google,发现两个设备的setBitOrder似乎不同,PN532是LSBFIRST,而网络是WSBFIRST,

找到原因后就简单了,只需要启动不同设备的时候,不仅仅设置SS,还要重新设置SPI的参数。

找到PN532.cpp的begin(),大约在43行,将

pn532_SPI.setDataMode(SPI_MODE0);
pn532_SPI.setBitOrder(LSBFIRST);
/*Set the SPI frequency to be one sixteenth of the
frequency of the system clock*/
pn532_SPI.setClockDivider(SPI_CLOCK_DIV16);

注释掉,然后在自己的程序里面分别写上

void enablePN() {
digitalWrite(ETH_SS, HIGH);
digitalWrite(NFC_SS, LOW);
SPI.setDataMode(SPI_MODE0);
SPI.setBitOrder(LSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV16);
delay(10);
}

void enableETH() {
digitalWrite(ETH_SS, LOW);
digitalWrite(NFC_SS, HIGH);
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV4);
SPI.setDataMode(SPCR & SPI_MODE_MASK);
SPCR &= ~(_BV(DORD));
SPI.setClockDivider( SPCR & SPI_CLOCK_MASK);
delay(10);
}

需要网卡的时候enableETH,需要NFC的时候启用enablePN,完美解决。

 

 

完整测试代码如下:

/* Web Server
* A simple web server that shows the value of the analog input pins.
*/

 

#include <SPI.h>
#include <Ethernet.h>
#include <PN532.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

byte ip[] = { 192, 168, 1, 15 };

EthernetServer server(80);
#define NFC_DEMO_DEBUG 1
#define PN532_CS 5
PN532 nfc(PN532_CS);

void initNFC()
{
#ifdef NFC_DEMO_DEBUG
Serial.println(“Begin start NFC!”);
#endif
nfc.begin();

uint32_t versiondata = nfc.getFirmwareVersion();
if (! versiondata) {
#ifdef NFC_DEMO_DEBUG
Serial.print(“Didn’t find PN53x board”);
#endif
//while (1); // halt
return;
// skip NFC, continue other step without NFC
}
#ifdef NFC_DEMO_DEBUG
// Got ok data, print it out!
Serial.print(“Found chip PN5”);
Serial.println((versiondata>>24) & 0xFF, HEX);
Serial.print(“Firmware ver. “);
Serial.print((versiondata>>16) & 0xFF, DEC);
Serial.print(‘.’);
Serial.println((versiondata>>8) & 0xFF, DEC);
Serial.print(“Supports “);
Serial.println(versiondata & 0xFF, HEX);
#endif
// configure board to read RFID tags and cards
nfc.SAMConfig();
}
void NFCReading()
{
uint32_t id;
// look for MiFare type cards
id = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A);

if (id != 0) {
#ifdef NFC_DEMO_DEBUG
Serial.print(“Read card #”);
Serial.println(id);
#endif
}
}
void WebPrint(EthernetClient client)
{

if (client) {
// an http request ends with a blank line
boolean current_line_is_blank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
// if we’ve gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so we can send a reply
if (c == ‘\n’ && current_line_is_blank) {
// send a standard http response header
client.println(“HTTP/1.1 200 OK”);
client.println(“Content-Type: text/html”);
client.println();

// output the value of each analog input pin
client.print(“welcome to tinyos”);
client.println(“<br />”);
client.print(“//*************************************”);
client.println(“<br />”);
client.print(“www.tinyos.net.cn”);
client.println(“<br />”);
client.print(“//*************************************”);
client.println(“<br />”);
for (int i = 0; i < 6; i++) {
client.print(“analog input “);
client.print(i);
client.print(” is “);
client.print(analogRead(i));
client.println(“<br />”);
}
break;
}
if (c == ‘\n’) {
// we’re starting a new line
current_line_is_blank = true;
} else if (c != ‘\r’) {
// we’ve gotten a character on the current line
current_line_is_blank = false;
}
}
}
client.stop();
}
}
#define ETH_SS 10
#define NFC_SS 5
void enablePN() {
digitalWrite(ETH_SS, HIGH);
digitalWrite(NFC_SS, LOW);
SPI.setDataMode(SPI_MODE0);
SPI.setBitOrder(LSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV16);
delay(10);
}

void enableETH() {
digitalWrite(ETH_SS, LOW);
digitalWrite(NFC_SS, HIGH);
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV4);
SPI.setDataMode(SPCR & SPI_MODE_MASK);
SPCR &= ~(_BV(DORD));
SPI.setClockDivider( SPCR & SPI_CLOCK_MASK);
delay(10);
}
void setup()
{
Serial.begin(9600);
pinMode(10, OUTPUT);
pinMode(5, OUTPUT);
pinMode(4, OUTPUT);
enableETH();
Ethernet.begin(mac, ip);
server.begin();
Serial.print(“server is at “);
Serial.println(Ethernet.localIP());
enablePN();
initNFC();
//digitalWrite(5,HIGH);
//digitalWrite(10,LOW);
}

void loop()
{
// pinMode(4,HIGH);
//digitalWrite(5,HIGH);
//digitalWrite(10,LOW);
enableETH();
EthernetClient client = server.available();
WebPrint(client);
enablePN();
//delay(100);
//digitalWrite(5,HIGH);
//digitalWrite(10,LOW);
NFCReading();
//delay(100);
}

 

后记:通过这种方式实现了网络、NFC、SD卡读写的协同工作。