Three problems fixed and your code works for me. First, you mistakenly used PINxx as the bit number almost everywhere:
Code: Select all
if (!(PIND & (1<<PIND0))) { // error
if (!(PIND & (1<<PD0))) { // correct
if (!(PIND & (1<<0))) { // what I use
The Pxx constants for port bits aren't very useful IMO. I just use the bit number. True, the Pxx constants shield code from Atmel some day putting bit 0 of a port on a different bit in the register, but that seems too remote to guard against.
The next problem is a minor logic error:
Code: Select all
uint8_t keyscan( void )
{
PORTB |= ( (1<<PB4) ); // PB4 Pull-Up einschalten
[...]
PORTB &= ~(1 << PB5); //Low
if (!(PIND & (1<<PD1))){
if (!(PINB & (1<<PB4))) {
return 1;
}
}
You check PB4 for low when you've got it pulled up. I thought you meant to check PB5, but that would be pointless since you know PB5 is low. I'm thinking maybe this is a check for multiple keys pressed (ghosting) so you don't rept a phantom key press. In that case, maybe you wanted if(PINB & (1<<PB4)).
Finally, put a delay after setting the scan lines, to give them time to reach their new levels. This is especially true if you're checking for phantom keys by using the one pulled up, as that will take a little time to reach high level. 200 microseconds works for me:
Code: Select all
uint8_t keyscan( void )
{
PORTB |= ( (1<<PB4) ); // PB4 Pull-Up einschalten
[...]
PORTB &= ~(1 << PB5); //Low
_delay_us( 200 ); // added
if (!(PIND & (1<<PD1))){
if ((PINB & (1<<PB4))) {
return 1;
}
}
One final change, as the original code doesn't do any debouncing. Only scan the keys when USB is ready for new updates. This puts an 8ms delay after key changes and avoids most bouncing:
Code: Select all
for(;;){ /* main event loop */
wdt_reset();
usbPoll();
if(TIFR & (1<<TOV0)){ /* 22 ms timer */
TIFR = 1<<TOV0;
if(idleRate != 0){
if(idleCounter > 4){
idleCounter -= 5; /* 22 ms in units of 4 ms */
}else{
idleCounter = idleRate;
keyDidChange = 1;
}
}
}
if(usbInterruptIsReady()){ // only scan if USB is ready
key = keyscan();
if(lastKey != key){
lastKey = key;
keyDidChange = 1;
}
if(keyDidChange){
keyDidChange = 0;
/* use last key and not current key status in order to avoid lost
changes in key status. */
buildReport(lastKey);
usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
}
}
}
Complete main.c:
Code: Select all
/* Name: main.c
* Project: HID-Test
* Author: Christian Starkjohann
* Creation Date: 2006-02-02
* Tabsize: 4
* Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id$
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <util/delay.h>
#include "usbdrv.h"
#include "oddebug.h"
/* ----------------------- hardware I/O abstraction ------------------------ */
/* pin assignments:
PB0 Key 1
PB1 Key 2
PB2 Key 3
PB3 Key 4
PB4 Key 5
PB5 Key 6
PC0 Key 7
PC1 Key 8
PC2 Key 9
PC3 Key 10
PC4 Key 11
PC5 Key 12
PD0 USB-
PD1 debug tx
PD2 USB+ (int0)
PD3 Key 13
PD4 Key 14
PD5 Key 15
PD6 Key 16
PD7 Key 17
*/
uint8_t keyscan( void )
{
DDRB &= ~( (1<<PB4) ); // PB4 als Eingaenge
PORTB |= ( (1<<PB4) ); // PB4 Pull-Up einschalten
DDRB |= (1 << PB5); // Ausgang
PORTB &= ~(1 << PB5); //Low
_delay_us( 200 );
if (!(PIND & (1<<PD1))){
if ((PINB & (1<<PB4))) {
return 1;
}
}
if (!(PIND & (1<<PD0))) {
if ((PINB & (1<<PB4))) {
return 2;
}
}
if (!(PINB & (1<<PB2))) {
if ((PINB & (1<<PB4))) {
return 3;
}
}
if (!(PINB & (1<<PB3))) {
if ((PINB & (1<<PB4))) {
return 4;
}
}
DDRB &= ~( (1<<PB5) ); // PB5 als Eingaenge
PORTB |= ( (1<<PB5) ); // PB5 Pull-Up einschalten
DDRB |= (1 << PB4); // Ausgang
PORTB &= ~(1 << PB4); //Low
_delay_us( 200 );
if (!(PIND & (1<<PD1))) {
if ((PINB & (1<<PB5))) {
return 5;
}
}
if (!(PIND & (1<<PD0))) {
if ((PINB & (1<<PB5))) {
return 6;
}
}
if (!(PINB & (1<<PB2))) {
if ((PINB & (1<<PB5))) {
return 7;
}
}
if (!(PINB & (1<<PB3))) {
if ((PINB & (1<<PB5))) {
return 8;
}
}
return 0;
}
static void hardwareInit(void)
{
DDRC |= (1 << PC0); // Ausgang
PORTC &= ~(1 << PC0); //PB1 High
DDRC |= (1 << PC1); // Ausgang
PORTC &= ~(1 << PC1); //PB1 High
DDRB &= ~( (1<<PB2) ); // PB2 als Eingaenge
PORTB |= ( (1<<PB2) ); // PB2 Pull-Up einschalten
DDRB &= ~( (1<<PB3) ); // PB3 als Eingaenge
PORTB |= ( (1<<PB3) ); // PB3 Pull-Up einschalten
DDRD &= ~( (1<<PD0) ); // PD0 als Eingaenge
PORTD |= ( (1<<PD0) ); // PD0 Pull-Up einschalten
DDRD &= ~( (1<<PD1) ); // PD1 als Eingaenge
PORTD |= ( (1<<PD1) ); // PD1 Pull-Up einschalten
/* configure timer 0 for a rate of 12M/(1024 * 256) = 45.78 Hz (~22ms) */
TCCR0 = 5; /* timer 0 prescaler: 1024 */
}
/* ------------------------------------------------------------------------- */
#define NUM_KEYS 8
/* ------------------------------------------------------------------------- */
/* ----------------------------- USB interface ----------------------------- */
/* ------------------------------------------------------------------------- */
static uchar reportBuffer[2]; /* buffer for HID reports */
static uchar idleRate; /* in 4 ms units */
const PROGMEM char usbHidReportDescriptor[35] = { /* USB report descriptor */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0 // END_COLLECTION
};
/* We use a simplifed keyboard report descriptor which does not support the
* boot protocol. We don't allow setting status LEDs and we only allow one
* simultaneous key press (except modifiers). We can therefore use short
* 2 byte input reports.
* The report descriptor has been created with usb.org's "HID Descriptor Tool"
* which can be downloaded from http://www.usb.org/developers/hidpage/.
* Redundant entries (such as LOGICAL_MINIMUM and USAGE_PAGE) have been omitted
* for the second INPUT item.
*/
/* Keyboard usage values, see usb.org's HID-usage-tables document, chapter
* 10 Keyboard/Keypad Page for more codes.
*/
#define MOD_CONTROL_LEFT (1<<0)
#define MOD_SHIFT_LEFT (1<<1)
#define MOD_ALT_LEFT (1<<2)
#define MOD_GUI_LEFT (1<<3)
#define MOD_CONTROL_RIGHT (1<<4)
#define MOD_SHIFT_RIGHT (1<<5)
#define MOD_ALT_RIGHT (1<<6)
#define MOD_GUI_RIGHT (1<<7)
#define KEY_A 4
#define KEY_B 5
#define KEY_C 6
#define KEY_D 7
#define KEY_E 8
#define KEY_F 9
#define KEY_G 10
#define KEY_H 11
#define KEY_I 12
#define KEY_J 13
#define KEY_K 14
#define KEY_L 15
#define KEY_M 16
#define KEY_N 17
#define KEY_O 18
#define KEY_P 19
#define KEY_Q 20
#define KEY_R 21
#define KEY_S 22
#define KEY_T 23
#define KEY_U 24
#define KEY_V 25
#define KEY_W 26
#define KEY_X 27
#define KEY_Y 28
#define KEY_Z 29
#define KEY_1 30
#define KEY_2 31
#define KEY_3 32
#define KEY_4 33
#define KEY_5 34
#define KEY_6 35
#define KEY_7 36
#define KEY_8 37
#define KEY_9 38
#define KEY_0 39
#define KEY_F1 58
#define KEY_F2 59
#define KEY_F3 60
#define KEY_F4 61
#define KEY_F5 62
#define KEY_F6 63
#define KEY_F7 64
#define KEY_F8 65
#define KEY_F9 66
#define KEY_F10 67
#define KEY_F11 68
#define KEY_F12 69
static const uchar keyReport[NUM_KEYS + 1][2] PROGMEM = {
/* none */ {0, 0}, /* no key pressed */
/* 1 */ {MOD_SHIFT_LEFT, KEY_A},
/* 2 */ {MOD_SHIFT_LEFT, KEY_B},
/* 3 */ {MOD_SHIFT_LEFT, KEY_C},
/* 4 */ {MOD_SHIFT_LEFT, KEY_D},
/* 5 */ {MOD_SHIFT_LEFT, KEY_E},
/* 6 */ {MOD_SHIFT_LEFT, KEY_F},
/* 7 */ {MOD_SHIFT_LEFT, KEY_G},
/* 8 */ {MOD_SHIFT_LEFT, KEY_H},
};
static void buildReport(uchar key)
{
/* This (not so elegant) cast saves us 10 bytes of program memory */
*(int *)reportBuffer = pgm_read_word(keyReport[key]);
}
uchar usbFunctionSetup(uchar data[8])
{
usbRequest_t *rq = (void *)data;
usbMsgPtr = reportBuffer;
if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* class request type */
if(rq->bRequest == USBRQ_HID_GET_REPORT){ /* wValue: ReportType (highbyte), ReportID (lowbyte) */
/* we only have one report type, so don't look at wValue */
buildReport(keyscan());
return sizeof(reportBuffer);
}else if(rq->bRequest == USBRQ_HID_GET_IDLE){
usbMsgPtr = &idleRate;
return 1;
}else if(rq->bRequest == USBRQ_HID_SET_IDLE){
idleRate = rq->wValue.bytes[1];
}
}else{
/* no vendor specific requests implemented */
}
return 0;
}
/* ------------------------------------------------------------------------- */
int main(void)
{
uchar key, lastKey = 0, keyDidChange = 0;
uchar idleCounter = 0;
wdt_enable(WDTO_2S);
hardwareInit();
odDebugInit();
usbInit();
sei();
DBG1(0x00, 0, 0);
for(;;){ /* main event loop */
wdt_reset();
usbPoll();
if(TIFR & (1<<TOV0)){ /* 22 ms timer */
TIFR = 1<<TOV0;
if(idleRate != 0){
if(idleCounter > 4){
idleCounter -= 5; /* 22 ms in units of 4 ms */
}else{
idleCounter = idleRate;
keyDidChange = 1;
}
}
}
if(usbInterruptIsReady()){
key = keyscan();
if(lastKey != key){
lastKey = key;
keyDidChange = 1;
}
if(keyDidChange){
keyDidChange = 0;
/* use last key and not current key status in order to avoid lost
changes in key status. */
buildReport(lastKey);
usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
}
}
}
return 0;
}
/* ------------------------------------------------------------------------- */