--- v1.4 (April 20, 2013)

- Support for Atmega168. Use Makefile.atmega168.
- New USB VID/PID
- Simplified main loop by remving setIdle/getIdle implementation. Optional for
  joysticks according to HID specification appendix G.
This commit is contained in:
Raphaël Assénat 2013-04-20 17:24:53 +00:00
parent 30ded39aad
commit befc9be7f1
7 changed files with 198 additions and 148 deletions

View File

@ -1,3 +1,9 @@
--- v1.4 (April 20, 2013)
- Support for Atmega168. Use Makefile.atmega168.
- New USB VID/PID
- Simplified main loop by remving setIdle/getIdle implementation. Optional for
joysticks according to HID specification appendix G.
--- v1.3 (April 2012)
- Added support for SNES multitaps in 4 player mode.

View File

@ -1,75 +0,0 @@
# Name: Makefile
# Project: HIDKeys
# Author: Christian Starkjohann
# Creation Date: 2006-02-02
# Tabsize: 4
# Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH
# License: Proprietary, free under certain conditions. See Documentation.
# This Revision: $Id: Makefile,v 1.5 2009-05-02 14:02:52 cvs Exp $
UISP = uisp -dprog=stk500 -dpart=atmega8 -dserial=/dev/avr
COMPILE = avr-gcc -Wall -Os -Iusbdrv -I. -mmcu=atmega8 -DF_CPU=12000000L #-DDEBUG_LEVEL=1
COMMON_OBJS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o
OBJECTS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o fournsnes.o devdesc.o
HEXFILE=main.hex
# symbolic targets:
all: $(HEXFILE)
.c.o:
$(COMPILE) -c $< -o $@
.S.o:
$(COMPILE) -x assembler-with-cpp -c $< -o $@
# "-x assembler-with-cpp" should not be necessary since this is the default
# file type for the .S (with capital S) extension. However, upper case
# characters are not always preserved on Windows. To ensure WinAVR
# compatibility define the file type manually.
.c.s:
$(COMPILE) -S $< -o $@
clean:
rm -f $(HEXFILE) main.lst main.obj main.cof main.list main.map main.eep.hex main.bin *.o usbdrv/*.o main.s usbdrv/oddebug.s usbdrv/usbdrv.s
# file targets:
main.bin: $(COMMON_OBJS) fournsnes.o devdesc.o
$(COMPILE) -o main.bin $(OBJECTS) -Wl,-Map=main.map
$(HEXFILE): main.bin
rm -f $(HEXFILE) main.eep.hex
avr-objcopy -j .text -j .data -O ihex main.bin $(HEXFILE)
./checksize main.bin
flash: all
#$(UISP) --erase --upload --verify if=$(HEXFILE)
$(UISP) --erase --upload if=$(HEXFILE)
flash_usb:
sudo avrdude -p m8 -P usb -c avrispmkII -Uflash:w:$(HEXFILE) -B 1.0
# Fuse high byte:
# 0xc9 = 1 1 0 0 1 0 0 1 <-- BOOTRST (boot reset vector at 0x0000)
# ^ ^ ^ ^ ^ ^ ^------ BOOTSZ0
# | | | | | +-------- BOOTSZ1
# | | | | + --------- EESAVE (don't preserve EEPROM over chip erase)
# | | | +-------------- CKOPT (full output swing)
# | | +---------------- SPIEN (allow serial programming)
# | +------------------ WDTON (WDT not always on)
# +-------------------- RSTDISBL (reset pin is enabled)
# Fuse low byte:
# 0x9f = 1 0 0 1 1 1 1 1
# ^ ^ \ / \--+--/
# | | | +------- CKSEL 3..0 (external >8M crystal)
# | | +--------------- SUT 1..0 (crystal osc, BOD enabled)
# | +------------------ BODEN (BrownOut Detector enabled)
# +-------------------- BODLEVEL (2.7V)
fuse:
$(UISP) --wr_fuse_h=0xc9 --wr_fuse_l=0x9f
fuse_usb:
sudo avrdude -p m8 -P usb -c avrispmkII -Uhfuse:w:0xc9:m -Ulfuse:w:0x9f:m -B 10.0

71
Makefile.atmega168 Normal file
View File

@ -0,0 +1,71 @@
CC=avr-gcc
AS=$(CC)
LD=$(CC)
PROGNAME=4nes4snes-m168
CPU=atmega168
CFLAGS=-Wall -Os -Iusbdrv -I. -mmcu=$(CPU) -DF_CPU=12000000L #-DDEBUG_LEVEL=1
LDFLAGS=-Wl,-Map=$(PROGNAME).map -mmcu=$(CPU)
AVRDUDE=avrdude -p m168 -P usb -c avrispmkII
OBJS=usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o fournsnes.o devdesc.o
HEXFILE=$(PROGNAME).hex
ELFFILE=$(PROGNAME).elf
# symbolic targets:
all: $(HEXFILE)
.c.o:
$(CC) $(CFLAGS) -c $< -o $@
.S.o:
$(CC) $(CFLAGS) -x assembler-with-cpp -c $< -o $@
# "-x assembler-with-cpp" should not be necessary since this is the default
# file type for the .S (with capital S) extension. However, upper case
# characters are not always preserved on Windows. To ensure WinAVR
# compatibility define the file type manually.
.c.s:
$(CC) $(CFLAGS) -S $< -o $@
clean:
rm -f $(HEXFILE) main.lst main.obj main.cof main.list main.map main.eep.hex main.bin *.o usbdrv/*.o main.s usbdrv/oddebug.s usbdrv/usbdrv.s
# file targets:
$(ELFFILE): $(OBJS)
$(LD) $(LDFLAGS) -o $(ELFFILE) $(OBJS)
$(HEXFILE): $(ELFFILE)
rm -f $(HEXFILE)
avr-objcopy -j .text -j .data -O ihex $(ELFFILE) $(HEXFILE)
./checksize $(ELFFILE)
flash: $(HEXFILE)
$(AVRDUDE) -Uflash:w:$(HEXFILE) -B 1.0
# - - - - - BOOTSZ1 BOOTSZ0 BOOTRST
# 0 0 0 0 0 0 0 1
EFUSE=0x01
# RSTDISBL DWEN SPIEN WDTON EESAVE BODLEVEL2 BODLEVEL1 BODLEVEL0
# 1 1 0 1 1 1 1 1
HFUSE=0xd5
#
# CKDIV8 CKOUT SUT1 SUT0 CKSEL3 CKSEL2 CKSEL1 CKSEL0
# 1 1 0 1 0 1 1 1
#
# Full swing crystal oscillator, BOD enabled
LFUSE=0xDF
fuse:
$(AVRDUDE) -e -Uefuse:w:$(EFUSE):m -Uhfuse:w:$(HFUSE):m -Ulfuse:w:$(LFUSE):m -B 20.0 -v
chip_erase:
$(AVRDUDE) -e -B 1.0 -F
reset:
$(AVRDUDE) -B 1.0 -F

77
Makefile.atmega8 Normal file
View File

@ -0,0 +1,77 @@
CC=avr-gcc
AS=$(CC)
LD=$(CC)
PROGNAME=4nes4snes-m8
CPU=atmega8
CFLAGS=-Wall -Os -Iusbdrv -I. -mmcu=$(CPU) -DF_CPU=12000000L #-DDEBUG_LEVEL=1
LDFLAGS=-Wl,-Map=$(PROGNAME).map -mmcu=$(CPU)
AVRDUDE=avrdude -p m8 -P usb -c avrispmkII
OBJS=usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o fournsnes.o devdesc.o
HEXFILE=$(PROGNAME).hex
ELFFILE=$(PROGNAME).elf
# symbolic targets:
all: $(HEXFILE)
.c.o:
$(CC) $(CFLAGS) -c $< -o $@
.S.o:
$(CC) $(CFLAGS) -x assembler-with-cpp -c $< -o $@
# "-x assembler-with-cpp" should not be necessary since this is the default
# file type for the .S (with capital S) extension. However, upper case
# characters are not always preserved on Windows. To ensure WinAVR
# compatibility define the file type manually.
.c.s:
$(CC) $(CFLAGS) -S $< -o $@
clean:
rm -f $(HEXFILE) main.lst main.obj main.cof main.list main.map main.eep.hex main.bin *.o usbdrv/*.o main.s usbdrv/oddebug.s usbdrv/usbdrv.s
# file targets:
$(ELFFILE): $(OBJS)
$(LD) $(LDFLAGS) -o $(ELFFILE) $(OBJS)
$(HEXFILE): $(ELFFILE)
rm -f $(HEXFILE)
avr-objcopy -j .text -j .data -O ihex $(ELFFILE) $(HEXFILE)
./checksize $(ELFFILE)
flash: $(HEXFILE)
$(AVRDUDE) -Uflash:w:$(HEXFILE) -B 1.0
# Fuse high byte:
# 0xc9 = 1 1 0 0 1 0 0 1 <-- BOOTRST (boot reset vector at 0x0000)
# ^ ^ ^ ^ ^ ^ ^------ BOOTSZ0
# | | | | | +-------- BOOTSZ1
# | | | | + --------- EESAVE (don't preserve EEPROM over chip erase)
# | | | +-------------- CKOPT (full output swing)
# | | +---------------- SPIEN (allow serial programming)
# | +------------------ WDTON (WDT not always on)
# +-------------------- RSTDISBL (reset pin is enabled)
# Fuse low byte:
# 0x9f = 1 0 0 1 1 1 1 1
# ^ ^ \ / \--+--/
# | | | +------- CKSEL 3..0 (external >8M crystal)
# | | +--------------- SUT 1..0 (crystal osc, BOD enabled)
# | +------------------ BODEN (BrownOut Detector enabled)
# +-------------------- BODLEVEL (2.7V)
LFUSE=0x9F
HFUSE=0xC9
fuse:
$(AVRDUDE) -e -Uhfuse:w:$(HFUSE):m -Ulfuse:w:$(LFUSE):m -B 20.0 -v
chip_erase:
$(AVRDUDE) -e -B 1.0 -F
reset:
$(AVRDUDE) -B 1.0 -F

View File

@ -12,8 +12,8 @@ Table of contents:
1) What is 4nes4snes?
--------------------
4nes4snes if a firmware for Atmel ATmega8 which
allows one to connect up to 4 NES and/or SNES
4nes4snes if a firmware for Atmel ATmega8 and Atmega168*
which allows one to connect up to 4 NES and/or SNES
controllers to a PC using a single circuit.
The device connects to an USB port and appears to the
@ -21,6 +21,9 @@ Table of contents:
that it looks like 4 controllers in Windows's
control_panel->game_controllers window.
* Other devices from the same family are probably supported, but
not tested.
2) USB Implementation
------------------
@ -48,15 +51,13 @@ Table of contents:
4) License
-------
4nes4snes is released under Objective Development's extended GPL
license. See License.txt
4nes4snes is released under the GPLv2 license. See License.txt
5) About the vendor id/product id pair:
------------------------------------
Please dont re-use them for other projects. Instead,
obtain your own. I got mine from mecanique:
http://www.mecanique.co.uk/products/usb/pid.html
Please do not re-use them for other projects. Instead,
obtain your own.
6) Where can I get more information and updates?

83
main.c
View File

@ -14,7 +14,6 @@
#include <string.h>
#include "usbdrv.h"
#include "oddebug.h"
#include "gamepad.h"
#include "fournsnes.h"
@ -27,6 +26,15 @@ static uchar rt_usbHidReportDescriptorSize=0;
static uchar *rt_usbDeviceDescriptor=NULL;
static uchar rt_usbDeviceDescriptorSize=0;
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega168A__) || \
defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328__) || \
defined(__AVR_ATmega328P__) || defined(__AVR_ATmega88__) || \
defined(__AVR_ATmega88A__) || defined(__AVR_ATmega88P__) || \
defined(__AVR_ATmega88PA__)
#define AT168_COMPATIBLE
#endif
/* The maximum number of independent reports that are supported. */
#define MAX_REPORTS 8
@ -118,14 +126,30 @@ static void hardwareInit(void)
while(--i); /* delay >10ms for USB reset */
}
DDRD = 0x00; /* 0000 0000 bin: remove USB reset condition */
/* configure timer 0 for a rate of 12M/(1024 * 256) = 45.78 Hz (~22ms) */
TCCR0 = 5; /* timer 0 prescaler: 1024 */
#if defined(AT168_COMPATIBLE)
TCCR2A= (1<<WGM21);
TCCR2B=(1<<CS22)|(1<<CS21)|(1<<CS20);
OCR2A=196; // for 60 hz
#else
TCCR2 = (1<<WGM21)|(1<<CS22)|(1<<CS21)|(1<<CS20);
OCR2 = 196; // for 60 hz
#endif
}
#if defined(AT168_COMPATIBLE)
#define mustPollControllers() (TIFR2 & (1<<OCF2A))
#define clrPollControllers() do { TIFR2 = 1<<OCF2A; } while(0)
#else
#define mustPollControllers() (TIFR & (1<<OCF2))
#define clrPollControllers() do { TIFR = 1<<OCF2; } while(0)
#endif
static uchar reportBuffer[12]; /* buffer for HID reports */
@ -134,8 +158,6 @@ static uchar reportBuffer[12]; /* buffer for HID reports */
/* ----------------------------- USB interface ----------------------------- */
/* ------------------------------------------------------------------------- */
static uchar idleRates[MAX_REPORTS]; /* in 4 ms units */
uchar usbFunctionDescriptor(struct usbRequest *rq)
{
if ((rq->bmRequestType & USBRQ_TYPE_MASK) != USBRQ_TYPE_STANDARD)
@ -167,7 +189,6 @@ static uchar reportPos=0;
uchar usbFunctionSetup(uchar data[8])
{
usbRequest_t *rq = (void *)data;
int i;
usbMsgPtr = reportBuffer;
@ -180,24 +201,6 @@ uchar usbFunctionSetup(uchar data[8])
reportPos=0;
return curGamepad->buildReport(reportBuffer, rq->wValue.bytes[0]);
case USBRQ_HID_GET_IDLE:
if (rq->wValue.bytes[0] > 0 && rq->wValue.bytes[0] <= MAX_REPORTS) {
usbMsgPtr = idleRates + (rq->wValue.bytes[0] - 1);
return 1;
}
break;
case USBRQ_HID_SET_IDLE:
if (rq->wValue.bytes[0]==0) {
for (i=0; i<MAX_REPORTS; i++)
idleRates[i] = rq->wValue.bytes[1];
}
else {
if (rq->wValue.bytes[0] > 0 && rq->wValue.bytes[0] <= MAX_REPORTS) {
idleRates[rq->wValue.bytes[0]-1] = rq->wValue.bytes[1];
}
}
break;
}
} else {
/* no vendor specific requests implemented */
@ -264,14 +267,11 @@ int main(void)
//wdt_enable(WDTO_2S);
hardwareInit();
curGamepad->init();
odDebugInit();
usbInit();
curGamepad->update();
sei();
DBG1(0x00, 0, 0);
for(;;){ /* main event loop */
wdt_reset();
@ -280,10 +280,10 @@ int main(void)
usbPoll();
/* Read the controllers at 60hz */
if (TIFR & (1<<OCF2))
if (mustPollControllers())
{
TIFR = 1<<OCF2;
clrPollControllers();
curGamepad->update();
/* Check what will have to be reported */
@ -294,27 +294,6 @@ int main(void)
}
}
/* Try to report at the granularity requested by
* the host. */
if (TIFR & (1<<TOV0)) { /* 22 ms timer */
TIFR = 1<<TOV0;
for (i=0; i<curGamepad->num_reports; i++)
{
// 0 means
if(idleRates[i] != 0){
if (idleCounters[i] > 4) {
idleCounters[i] -= 5; /* 22 ms in units of 4 ms */
} else {
// reset the counter and schedule a report for this
idleCounters[i] = idleRates[i];
must_report |= (1<<i);
}
}
}
}
if(must_report)
{

View File

@ -5,7 +5,7 @@
* Tabsize: 4
* Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
* License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
* This Revision: $Id: usbconfig.h,v 1.8 2012-04-09 04:05:43 cvs Exp $
* This Revision: $Id: usbconfig.h,v 1.9 2013-04-20 17:24:53 cvs Exp $
*/
#ifndef __usbconfig_h_included__
@ -129,21 +129,12 @@ rename it to "usbconfig.h". Then edit it accordingly.
/* -------------------------- Device Description --------------------------- */
/* This vendor ID comes from "Mecanique" which used to sale
* ranges of product IDs. */
#define USB_CFG_VENDOR_ID 0x81, 0x17
/* This product ID is registered to the author. If you change the code
* and wish to distribute it, please obtain and use your own VID/PID. Otherwise,
* there are serious compatibility and conflict risks, under Windows
* in particular.
*
* See the technical notes in usbdrv/USBID-License.txt for more details.
*/
#define USB_CFG_DEVICE_ID 0x9d, 0x0a
/* This is the Dracal technnologies inc. (Raphnet technologies) VID */
#define USB_CFG_VENDOR_ID 0x8B, 0x28
#define USB_CFG_DEVICE_ID 0x03, 0x00
#define USB_CFG_DEVICE_VERSION 0x03, 0x01
#define USB_CFG_DEVICE_VERSION 0x04, 0x01
/* Version number of the device: Minor number first, then major number.
*/
#define USB_CFG_VENDOR_NAME 'r', 'a', 'p', 'h', 'n', 'e', 't', '.', 'n', 'e', 't'