Difficulties Replicating C64 USB

General discussions about V-USB, our firmware-only implementation of a low speed USB device on Atmel's AVR microcontrollers
Lostchemist
Posts: 10
Joined: Wed Aug 27, 2014 1:16 am

Difficulties Replicating C64 USB

Post by Lostchemist » Wed Aug 27, 2014 2:19 am

I am attempting to replicate Spaceman Spiff's Commodore Keyboard to USB hack found here: http://symlink.dk/projects/c64key/. I have assembled a copy of the circuit he provides for the ATmega8-16PU http://symlink.dk/projects/c64key/mega8.png with minor changes (like changing the LED resistor value and omitting the jumper). I am relatively confident that the board is functional.

I have downloaded the project files Spiff provides and attempted to flash them to the mega. I am using an AVR Pocket Programmer, so my avrdude was changed to include

Code: Select all

-c usbtiny

Leaving the rest of the files as they are, I have successfully flashed the the chip and the programmer has confirmed file integrity. I have done this on two different chips. The compiler output reads:

Code: Select all

avr-gcc (WinAVR 20100110) 4.3.3
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


Compiling: main.c
avr-gcc -c -mmcu=atmega8 -I. -gdwarf-2 -DF_CPU=12000000UL -I../usbdrv  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=main.lst  -std=gnu99 -MD -MP -MF .dep/main.o.d main.c -o main.o -I../keymaps/ -include key_us_us.h

Compiling: ../usbdrv/usbdrv.c
avr-gcc -c -mmcu=atmega8 -I. -gdwarf-2 -DF_CPU=12000000UL -I../usbdrv  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=../usbdrv/usbdrv.lst  -std=gnu99 -MD -MP -MF .dep/usbdrv.o.d ../usbdrv/usbdrv.c -o ../usbdrv/usbdrv.o

Compiling: ../usbdrv/oddebug.c
avr-gcc -c -mmcu=atmega8 -I. -gdwarf-2 -DF_CPU=12000000UL -I../usbdrv  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=../usbdrv/oddebug.lst  -std=gnu99 -MD -MP -MF .dep/oddebug.o.d ../usbdrv/oddebug.c -o ../usbdrv/oddebug.o

Assembling: ../usbdrv/usbdrvasm.S
avr-gcc -c -mmcu=atmega8 -I. -x assembler-with-cpp -Wa,-adhlns=../usbdrv/usbdrvasm.lst,-gstabs  ../usbdrv/usbdrvasm.S -o ../usbdrv/usbdrvasm.o

Linking: main.elf
avr-gcc -mmcu=atmega8 -I. -gdwarf-2 -DF_CPU=12000000UL -I../usbdrv  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=../usbdrv/usbdrv.o  -std=gnu99 -MD -MP -MF .dep/main.elf.d ../usbdrv/usbdrv.o ../usbdrv/oddebug.o main.o ../usbdrv/usbdrvasm.o --output main.elf -Wl,-Map=main.map,--cref  -Wl,-u,vfprintf -lprintf_min -Wl,-u,vfscanf -lscanf_min -lm

Creating load file for Flash: main.hex
avr-objcopy -O srec -R .eeprom main.elf main.hex

Creating load file for EEPROM: main.eep
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" \
   --change-section-lma .eeprom=0 -O srec main.elf main.eep
c:\WinAVR-20100110\bin\avr-objcopy.exe: --change-section-lma .eeprom=0x00000000 never used

Creating Extended Listing: main.lss
avr-objdump -h -S main.elf > main.lss

Creating Symbol Table: main.sym
avr-nm -n main.elf > main.sym

Size after:
main.elf  :
section            size      addr
.text              4912         0
.data                14   8388704
.bss                 65   8388718
.stab              4440         0
.stabstr             71         0
.debug_aranges       64         0
.debug_pubnames     636         0
.debug_info        4571         0
.debug_abbrev      1258         0
.debug_line        2076         0
.debug_frame        128         0
.debug_str         2213         0
.debug_loc         1853         0
.debug_ranges       232         0
Total             22533



-------- end --------


during the make process and

Code: Select all

avrdude -p atmega8 -P com1     -c usbtiny    -U flash:w:main.hex  

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e9307
avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "main.hex"
avrdude: input file main.hex auto detected as Motorola S-Record
avrdude: writing flash (4926 bytes):

Writing | ################################################## | 100% 9.75s

avrdude: 4926 bytes of flash written
avrdude: verifying flash memory against main.hex:
avrdude: load data flash data from input file main.hex:
avrdude: input file main.hex auto detected as Motorola S-Record
avrdude: input file main.hex contains 4926 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 6.19s

avrdude: verifying ...
avrdude: 4926 bytes of flash verified

avrdude done.  Thank you.


when flashing to the avr. No errors jump out at me.

When I plug my device into the USB port, I get

Code: Select all

Error 43

Windows has stopped this device because it has reported problems. (Code 43)


Windows is trying to use usbfilter.sys as a driver.

Where I am I going wrong? This seems like it should work.


Solutions attempted:
  • Messing with the fuses (couldn't find a successful setting)
  • Plugging into Ubuntu box (unsuccessful)
  • Plugging into RasPi (didn't think it would work anyway)
  • Changing IC's (same response)
  • Changing 12Mhz crystal (same response)
  • Confirming I didn't mix up D- and D+ (caused Error 43 to loop constantly)
  • USBlyze (no useful data could be gleaned since the program reported that the unknown device was shown to be constantly in a state of reset)
  • Starting over and building the same circuit from scratch on a printed PC instead of a wired perf board (I needed soldering practice anyway)

blargg
Rank 3
Rank 3
Posts: 102
Joined: Thu Nov 14, 2013 10:01 pm

Re: Difficulties Replicating C64 USB

Post by blargg » Wed Aug 27, 2014 3:24 am

I'm using Ubuntu and tried the linked version (with a couple of mode to make it run on a USBasp board), and got an error when connecting it. I noticed it's using an old version of V-USB. Using the latest (20121206) it is properly recognized as a USB keyboard:

Code: Select all

Aug 26 20:19:26 optiplex kernel: [180787.088014] usb 6-2: new low-speed USB device number 56 using uhci_hcd
Aug 26 20:19:26 optiplex kernel: [180787.297275] input: Spaceman Spiff http://symlink.dk Spiff's C64 Keyboard as /devices/pci0000:00/0000:00:1d.1/usb6/6-2/6-2:1.0/input/input20
Aug 26 20:19:26 optiplex kernel: [180787.297347] generic-usb 0003:4242:BABE.0013: input,hidraw2: USB HID v1.01 Keyboard [Spaceman Spiff http://symlink.dk Spiff's C64 Keyboard] on usb-0000:00:1d.1-2/input0


I simply replaced usbdrv/ with the latest, and had to add these lines to usbconfig.h:

Code: Select all

#define F_CPU 12000000UL
#define USB_CFG_CLOCK_KHZ       (F_CPU/1000)


The extra F_CPU definition shouldn't be necessary there, but I just couldn't get the one from the Makefile to work (usbdrv kept complaining about an unsupported CPU rate).

Lostchemist
Posts: 10
Joined: Wed Aug 27, 2014 1:16 am

Re: Difficulties Replicating C64 USB

Post by Lostchemist » Wed Aug 27, 2014 8:20 pm

Blargg,

I implemented your changes. The V-USB update was something I should have thought of earlier. The extra F_CPU definition is probably required because the clock speed entry in the makefile omits the UL. I also had to tell the code that char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] was actually a constant. I'm surprised you didn't run into that error if you used the main.c file.

The changes were unsuccessful on all three systems I tried.

Ubuntu gave nothing on lsusb, and syslog reported: device descriptor read/64 error 71
Apparently the device was reset several times before it gave up.

RasPi gave nothing on lsusb, and messages contained four attempts at connection saying: new low-speed USB device number X using dwc_otg

Windows 7 just said that the device was malfunctioning, error 43. Very descriptive. Thanks Windows.



So anyway, I am still having trouble with the error. My PI said he would take a look at the files while he was in a meeting tomorrow, so hopefully he will have something to input. Maybe someone else out there will have a suggestion.

blargg
Rank 3
Rank 3
Posts: 102
Joined: Thu Nov 14, 2013 10:01 pm

Re: Difficulties Replicating C64 USB

Post by blargg » Wed Aug 27, 2014 8:57 pm

Here is the entire diff (besides swapping in the newer usbdrv) I ran on my USBasp stick. I disabled key scanning because nothing is connected. Patch with cd c64key && patch -p1 <../c64key-usbasp.diff

Code: Select all

diff -rupN c64key/m8key/main.c c64key-usbasp/m8key/main.c
--- c64key/m8key/main.c   2007-12-09 15:13:40.000000000 -0600
+++ c64key-usbasp/m8key/main.c   2014-08-27 13:49:06.932996357 -0500
@@ -87,7 +87,7 @@ const char modmask[8] PROGMEM = {
 /* USB report descriptor (length is defined in usbconfig.h)
    This has been changed to conform to the USB keyboard boot
    protocol */
-char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH]
+const char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH]
   PROGMEM = {
     0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
     0x09, 0x06,                    // USAGE (Keyboard)
@@ -132,6 +132,7 @@ static uchar idleRate;           /* in 4
 static uchar protocolVer=1;      /* 0 is the boot protocol, 1 is report protocol */
 
 static void hardwareInit(void) {
+#if 0
   PORTB = 0x3F;   /* Port B are row drivers */
   DDRB  = 0x00;   /* TODO: all pins output */
 
@@ -145,6 +146,7 @@ static void hardwareInit(void) {
   _delay_us(11);   /* delay >10ms for USB reset */
 
   DDRD = 0x02;    /* 0000 0010 bin: remove USB reset condition */
+#endif
   /* configure timer 0 for a rate of 12M/(1024 * 256) = 45.78 Hz (~22ms) */
   TCCR0 = 5;      /* timer 0 prescaler: 1024 */
 }
@@ -157,6 +159,7 @@ const char extrows[3] PROGMEM = { 0x10,
    if a key change has been found, a new report is generated, and the
    function returns true to signal the transfer of the report. */
 static uchar scankeys(void) {
+  return 0; // I don't have key matrix connected
   uchar reportIndex=1; /* First available report entry is 2 */
   uchar retval=0;
   uchar row,data,key, modkeys;
diff -rupN c64key/m8key/usbconfig.h c64key-usbasp/m8key/usbconfig.h
--- c64key/m8key/usbconfig.h   2007-12-09 15:13:50.000000000 -0600
+++ c64key-usbasp/m8key/usbconfig.h   2014-08-26 20:18:59.082568645 -0500
@@ -23,7 +23,7 @@ the newest features and options.
 
 /* ---------------------------- Hardware Config ---------------------------- */
 
-#define USB_CFG_IOPORTNAME      D
+#define USB_CFG_IOPORTNAME      B
 /* This is the port where the USB bus is connected. When you configure it to
  * "B", the registers PORTB, PINB and DDRB will be used.
  */
@@ -31,12 +31,15 @@ the newest features and options.
 /* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected.
  * This may be any bit in the port.
  */
-#define USB_CFG_DPLUS_BIT       2
+#define USB_CFG_DPLUS_BIT       1
 /* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected.
  * This may be any bit in the port. Please note that D+ must also be connected
  * to interrupt pin INT0!
  */
 
+#define F_CPU 12000000UL
+#define USB_CFG_CLOCK_KHZ       (F_CPU/1000)
+
 /* ----------------------- Optional Hardware Config ------------------------ */
 
 /* #define USB_CFG_PULLUP_IOPORTNAME   D */

Lostchemist
Posts: 10
Joined: Wed Aug 27, 2014 1:16 am

Re: Difficulties Replicating C64 USB

Post by Lostchemist » Wed Aug 27, 2014 11:45 pm

I manually patched your diff, but still nothing, same responses on all tested OS's. My code is as follows.

Makefile

Code: Select all

#####################################################################
# Makefile - Build-file for the GNU Make utility                    #
# $Id: $
# Version 1.98ß                                                     #
#####################################################################
# c64key is Copyright (C) 2006-2007 Mikkel Holm Olsen               #
# based on WinAVR Makefile Template written by Eric B. Weddington,  #
# Jörg Wunsch, et al.                                               #
#####################################################################
# Spaceman Spiff's Commodire 64 USB Keyboard (c64key for short) is  #
# is free software; you can redistribute it and/or modify it under  #
# the terms of the OBDEV license, as found in the licence.txt file. #
#                                                                   #
# c64key is distributed in the hope that it will be useful,         #
# but WITHOUT ANY WARRANTY; without even the implied warranty of    #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     #
# OBDEV license for further details.                                #
#####################################################################


#----------------------------------------------------------------------------
# On command line:
#
# make all = Make software.
#
# make clean = Clean out built project files.
#
# make coff = Convert ELF to AVR COFF.
#
# make extcoff = Convert ELF to AVR Extended COFF.
#
# make program = Download the hex file to the device, using avrdude.
#                Please customize the avrdude settings below first!
#
#
# make fuse = Set the fuses
#
# make debug = Start either simulavr or avarice as specified for debugging,
#              with avr-gdb or avr-insight as the front end for debugging.
#
# make filename.s = Just compile filename.c into the assembler code only.
#
# make filename.i = Create a preprocessed source file for use in submitting
#                   bug reports to the GCC project.
#
# To rebuild project do "make clean" then "make all".
#----------------------------------------------------------------------------

# The keyboard map to use, e.g. key_us_us.h
KEYMAP = key_us_us.h


# MCU name
MCU = atmega8


# Processor frequency.
#     This will define a symbol, F_CPU, in all source code files equal to the
#     processor frequency. You can then use this symbol in your source code to
#     calculate timings. Do NOT tack on a 'UL' at the end, this will be done
#     automatically to create a 32-bit value in your source code.
F_CPU = 12000000


# Output format. (can be srec, ihex, binary)
FORMAT = ihex


# Target file name (without extension).
TARGET = main


# List C source files here. (C dependencies are automatically generated.)
SRC = ../usbdrv/usbdrv.c ../usbdrv/oddebug.c $(TARGET).c

# List Assembler source files here.
#     Make them always end in a capital .S.  Files ending in a lowercase .s
#     will not be considered source files but generated files (assembler
#     output from the compiler), and will be deleted upon "make clean"!
#     Even though the DOS/Win* filesystem matches both .s and .S the same,
#     it will preserve the spelling of the filenames, and gcc itself does
#     care about how the name is spelled on its command-line.
ASRC = ../usbdrv/usbdrvasm.S


# Optimization level, can be [0, 1, 2, 3, s].
#     0 = turn off optimization. s = optimize for size.
#     (Note: 3 is not always the best optimization level. See avr-libc FAQ.)
OPT = s


# Debugging format.
#     Native formats for AVR-GCC's -g are dwarf-2 [default] or stabs.
#     AVR Studio 4.10 requires dwarf-2.
#     AVR [Extended] COFF format requires stabs, plus an avr-objcopy run.
DEBUG = dwarf-2


# List any extra directories to look for include files here.
#     Each directory must be seperated by a space.
#     Use forward slashes for directory separators.
#     For a directory that has spaces, enclose it in quotes.
EXTRAINCDIRS =


# Compiler flag to set the C Standard level.
#     c89   = "ANSI" C
#     gnu89 = c89 plus GCC extensions
#     c99   = ISO C99 standard (not yet fully implemented)
#     gnu99 = c99 plus GCC extensions
CSTANDARD = -std=gnu99


# Place -D or -U options here
CDEFS = -DF_CPU=$(F_CPU)UL


# Place -I options here
CINCS = -I../usbdrv



#---------------- Compiler Options ----------------
#  -g*:          generate debugging information
#  -O*:          optimization level
#  -f...:        tuning, see GCC manual and avr-libc documentation
#  -Wall...:     warning level
#  -Wa,...:      tell GCC to pass this to the assembler.
#    -adhlns...: create assembler listing
CFLAGS = -g$(DEBUG)
CFLAGS += $(CDEFS) $(CINCS)
CFLAGS += -O$(OPT)
CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
CFLAGS += -Wall -Wstrict-prototypes
CFLAGS += -Wa,-adhlns=$(<:.c=.lst)
CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
CFLAGS += $(CSTANDARD)


#---------------- Assembler Options ----------------
#  -Wa,...:   tell GCC to pass this to the assembler.
#  -ahlms:    create listing
#  -gstabs:   have the assembler create line number information; note that
#             for use in COFF files, additional information about filenames
#             and function names needs to be present in the assembler source
#             files -- see avr-libc docs [FIXME: not yet described there]
ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs


#---------------- Library Options ----------------
# Minimalistic printf version
PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min

# Floating point printf version (requires MATH_LIB = -lm below)
PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt

# If this is left blank, then it will use the Standard printf version.
#PRINTF_LIB =
PRINTF_LIB = $(PRINTF_LIB_MIN)
#PRINTF_LIB = $(PRINTF_LIB_FLOAT)


# Minimalistic scanf version
SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min

# Floating point + %[ scanf version (requires MATH_LIB = -lm below)
SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt

# If this is left blank, then it will use the Standard scanf version.
#SCANF_LIB =
SCANF_LIB = $(SCANF_LIB_MIN)
#SCANF_LIB = $(SCANF_LIB_FLOAT)


MATH_LIB = -lm



#---------------- External Memory Options ----------------

# 64 KB of external RAM, starting after internal RAM (ATmega128!),
# used for variables (.data/.bss) and heap (malloc()).
#EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff

# 64 KB of external RAM, starting after internal RAM (ATmega128!),
# only used for heap (malloc()).
#EXTMEMOPTS = -Wl,--defsym=__heap_start=0x801100,--defsym=__heap_end=0x80ffff

EXTMEMOPTS =


#---------------- Linker Options ----------------
#  -Wl,...:     tell GCC to pass this to linker.
#    -Map:      create map file
#    --cref:    add cross reference to  map file
LDFLAGS = -Wl,-Map=$(TARGET).map,--cref
LDFLAGS += $(EXTMEMOPTS)
LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB)



#---------------- Programming Options (avrdude) ----------------

# Programming hardware: alf avr910 avrisp bascom bsd
# dt006 pavr picoweb pony-stk200 sp12 stk200 stk500
#
# Type: avrdude -c ?
# to get a full listing.
#
AVRDUDE_PROGRAMMER = usbtiny

# com1 = serial port. Use lpt1 to connect to parallel port.
AVRDUDE_PORT = com1    # programmer connected to serial device

AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep
#AVRDUDE_LOCKBITS = -V -U lock:w:0xE8:m


# Uncomment the following if you want avrdude's erase cycle counter.
# Note that this counter needs to be initialized first using -Yn,
# see avrdude manual.
#AVRDUDE_ERASE_COUNTER = -y

# Uncomment the following if you do /not/ wish a verification to be
# performed after programming the device.
#AVRDUDE_NO_VERIFY = -V

# Increase verbosity level.  Please use this when submitting bug
# reports about avrdude. See <http://savannah.nongnu.org/projects/avrdude>
# to submit bug reports.
#AVRDUDE_VERBOSE = -v -v

AVRDUDE_FLAGS = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER)
AVRDUDE_FLAGS += $(AVRDUDE_NO_VERIFY)
AVRDUDE_FLAGS += $(AVRDUDE_VERBOSE)
AVRDUDE_FLAGS += $(AVRDUDE_ERASE_COUNTER)



#---------------- Debugging Options ----------------

# For simulavr only - target MCU frequency.
DEBUG_MFREQ = $(F_CPU)

# Set the DEBUG_UI to either gdb or insight.
# DEBUG_UI = gdb
DEBUG_UI = insight

# Set the debugging back-end to either avarice, simulavr.
DEBUG_BACKEND = avarice
#DEBUG_BACKEND = simulavr

# GDB Init Filename.
GDBINIT_FILE = __avr_gdbinit

# When using avarice settings for the JTAG
JTAG_DEV = /dev/com1

# Debugging port used to communicate between GDB / avarice / simulavr.
DEBUG_PORT = 4242

# Debugging host used to communicate between GDB / avarice / simulavr, normally
#     just set to localhost unless doing some sort of crazy debugging when
#     avarice is running on a different computer.
DEBUG_HOST = localhost



#============================================================================


# Define programs and commands.
SHELL = sh
CC = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
SIZE = avr-size
NM = avr-nm
AVRDUDE = avrdude
REMOVE = rm -f
COPY = cp
WINSHELL = cmd


# Define Messages
# English
MSG_ERRORS_NONE = Errors: none
MSG_BEGIN = -------- begin --------
MSG_END = --------  end  --------
MSG_SIZE_BEFORE = Size before:
MSG_SIZE_AFTER = Size after:
MSG_COFF = Converting to AVR COFF:
MSG_EXTENDED_COFF = Converting to AVR Extended COFF:
MSG_FLASH = Creating load file for Flash:
MSG_EEPROM = Creating load file for EEPROM:
MSG_EXTENDED_LISTING = Creating Extended Listing:
MSG_SYMBOL_TABLE = Creating Symbol Table:
MSG_LINKING = Linking:
MSG_COMPILING = Compiling:
MSG_ASSEMBLING = Assembling:
MSG_CLEANING = Cleaning project:




# Define all object files.
OBJ = $(SRC:.c=.o) $(ASRC:.S=.o)

# Define all listing files.
LST = $(SRC:.c=.lst) $(ASRC:.S=.lst)


# Compiler flags to generate dependency files.
GENDEPFLAGS = -MD -MP -MF .dep/$(@F).d


# Combine all necessary flags and optional flags.
# Add target processor to flags.
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)





# Default target.
all: begin gccversion sizebefore build sizeafter end

build: elf hex eep lss sym

elf: $(TARGET).elf
hex: $(TARGET).hex
eep: $(TARGET).eep
lss: $(TARGET).lss
sym: $(TARGET).sym



# Eye candy.
# AVR Studio 3.x does not check make's exit code but relies on
# the following magic strings to be generated by the compile job.
begin:
   @echo
   @echo $(MSG_BEGIN)

end:
   @echo $(MSG_END)
   @echo


# Display size of file.
HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex
ELFSIZE = $(SIZE) -A $(TARGET).elf
AVRMEM = avr-mem.sh $(TARGET).elf $(MCU)

sizebefore:
   @if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_BEFORE); $(ELFSIZE); \
   $(AVRMEM) 2>/dev/null; echo; fi

sizeafter:
   @if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_AFTER); $(ELFSIZE); \
   $(AVRMEM) 2>/dev/null; echo; fi



# Display compiler version information.
gccversion :
   @$(CC) --version



# Program the device. 
program: $(TARGET).hex $(TARGET).eep
   $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) $(AVRDUDE_WRITE_EEPROM) $(AVRDUDE_LOCKBITS)

# Set fuses.
# For ATmega8: boot flash 128 words, BODlevel=2.7V, BOD enabled,
# External crystal 1K CK + 4ms startup
fuse:
   $(AVRDUDE) $(AVRDUDE_FLAGS) -U hfuse:w:0xDF:m -U lfuse:w:0xBE:m

# Generate avr-gdb config/init file which does the following:
#     define the reset signal, load the target file, connect to target, and set
#     a breakpoint at main().
gdb-config:
   @$(REMOVE) $(GDBINIT_FILE)
   @echo define reset >> $(GDBINIT_FILE)
   @echo SIGNAL SIGHUP >> $(GDBINIT_FILE)
   @echo end >> $(GDBINIT_FILE)
   @echo file $(TARGET).elf >> $(GDBINIT_FILE)
   @echo target remote $(DEBUG_HOST):$(DEBUG_PORT)  >> $(GDBINIT_FILE)
ifeq ($(DEBUG_BACKEND),simulavr)
   @echo load  >> $(GDBINIT_FILE)
endif   
   @echo break main >> $(GDBINIT_FILE)
   
debug: gdb-config $(TARGET).elf
ifeq ($(DEBUG_BACKEND), avarice)
   @echo Starting AVaRICE - Press enter when "waiting to connect" message displays.
   @$(WINSHELL) /c start avarice --jtag $(JTAG_DEV) --erase --program --file \
   $(TARGET).elf $(DEBUG_HOST):$(DEBUG_PORT)
   @$(WINSHELL) /c pause
   
else
   @$(WINSHELL) /c start simulavr --gdbserver --device $(MCU) --clock-freq \
   $(DEBUG_MFREQ) --port $(DEBUG_PORT)
endif
   @$(WINSHELL) /c start avr-$(DEBUG_UI) --command=$(GDBINIT_FILE)
   



# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
COFFCONVERT=$(OBJCOPY) --debugging \
--change-section-address .data-0x800000 \
--change-section-address .bss-0x800000 \
--change-section-address .noinit-0x800000 \
--change-section-address .eeprom-0x810000


coff: $(TARGET).elf
   @echo
   @echo $(MSG_COFF) $(TARGET).cof
   $(COFFCONVERT) -O coff-avr $< $(TARGET).cof


extcoff: $(TARGET).elf
   @echo
   @echo $(MSG_EXTENDED_COFF) $(TARGET).cof
   $(COFFCONVERT) -O coff-ext-avr $< $(TARGET).cof



# Create final output files (.hex, .eep) from ELF output file.
%.hex: %.elf
   @echo
   @echo $(MSG_FLASH) $@
   $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@

%.eep: %.elf
   @echo
   @echo $(MSG_EEPROM) $@
   -$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
   --change-section-lma .eeprom=0 -O $(FORMAT) $< $@

# Create extended listing file from ELF output file.
%.lss: %.elf
   @echo
   @echo $(MSG_EXTENDED_LISTING) $@
   $(OBJDUMP) -h -S $< > $@

# Create a symbol table from ELF output file.
%.sym: %.elf
   @echo
   @echo $(MSG_SYMBOL_TABLE) $@
   $(NM) -n $< > $@



# Link: create ELF output file from object files.
.SECONDARY : $(TARGET).elf
.PRECIOUS : $(OBJ)
%.elf: $(OBJ)
   @echo
   @echo $(MSG_LINKING) $@
   $(CC) $(ALL_CFLAGS) $^ --output $@ $(LDFLAGS)


main.o : main.c
   @echo
   @echo $(MSG_COMPILING) $<
   $(CC) -c $(ALL_CFLAGS) $< -o $@ -I../keymaps/ -include $(KEYMAP)

# Compile: create object files from C source files.
%.o : %.c
   @echo
   @echo $(MSG_COMPILING) $<
   $(CC) -c $(ALL_CFLAGS) $< -o $@


# Compile: create assembler files from C source files.
%.s : %.c
   $(CC) -S $(ALL_CFLAGS) $< -o $@


# Assemble: create object files from assembler source files.
%.o : %.S
   @echo
   @echo $(MSG_ASSEMBLING) $<
   $(CC) -c $(ALL_ASFLAGS) $< -o $@

# Create preprocessed source for use in sending a bug report.
%.i : %.c
   $(CC) -E -mmcu=$(MCU) -I. $(CFLAGS) $< -o $@


# Target: clean project.
clean: begin clean_list end

clean_list :
   @echo
   @echo $(MSG_CLEANING)
#   $(REMOVE) $(TARGET).hex
   $(REMOVE) $(TARGET).eep
   $(REMOVE) $(TARGET).cof
   $(REMOVE) $(TARGET).elf
   $(REMOVE) $(TARGET).map
   $(REMOVE) $(TARGET).sym
   $(REMOVE) $(TARGET).lss
   $(REMOVE) $(OBJ)
   $(REMOVE) $(LST)
   $(REMOVE) $(SRC:.c=.s)
   $(REMOVE) $(SRC:.c=.d)
   $(REMOVE) .dep/*



# Include the dependency files.
-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*)


# Listing of phony targets.
.PHONY : all begin finish end sizebefore sizeafter gccversion \
build elf hex eep lss sym coff extcoff \
clean clean_list program debug gdb-config




Main.c

Code: Select all

/*********************************************************************
 * main.c - Main firmware (ATmega8 version)                          *
 * $Id: $
 * Version 1.98ß                                                     *
 *********************************************************************
 * c64key is Copyright (C) 2006-2007 Mikkel Holm Olsen               *
 * based on HID-Test by Christian Starkjohann, Objective Development *
 *********************************************************************
 * Spaceman Spiff's Commodire 64 USB Keyboard (c64key for short) is  *
 * is free software; you can redistribute it and/or modify it under  *
 * the terms of the OBDEV license, as found in the licence.txt file. *
 *                                                                   *
 * c64key is distributed in the hope that it will be useful,         *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of    *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     *
 * OBDEV license for further details.                                *
 *********************************************************************/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <util/delay.h>
#include <string.h>

/* Now included from the makefile */
//#include "keycodes.h"

#include "usbdrv.h"
#define DEBUG_LEVEL 0
#include "oddebug.h"

/* Hardware documentation:
 * ATmega-8 @12.000 MHz
 *
 * PB0..PB5: Keyboard matrix Row0..Row5 (pins 12,11,10,5,8,7 on C64 kbd)
 * PB6..PB7: 12MHz X-tal
 * PC0..PC5: Keyboard matrix Col0..Col5 (pins 13,19,18,17,16,15 on C64 kbd)
 * PD0     : D- USB negative (needs appropriate zener-diode and resistors)
 * PD1     : UART TX
 * PD2/INT0: D+ USB positive (needs appropriate zener-diode and resistors)
 * PD3     : Keyboard matrix Row8 (Restore key)
 * PD4     : Keyboard matrix Row6 (pin 6 on C64 kbd)
 * PD5     : Keyboard matrix Row7 (pin 9 on C64 kbd)
 * PD6     : Keyboard matrix Col6 (pin 14 on C64 kbd)
 * PD7     : Keyboard matrix Col7 (pin 20 on C64 kbd)
 *
 * USB Connector:
 * -------------
 *  1 (red)    +5V
 *  2 (white)  DATA-
 *  3 (green)  DATA+
 *  4 (black)  GND
 *   
 *
 *
 *                                     VCC
 *                  +--[4k7]--+--[2k2]--+
 *      USB        GND        |                     ATmega-8
 *                            |
 *      (D-)-------+----------+--------[82r]------- PD0
 *                 |
 *      (D+)-------|-----+-------------[82r]------- PD2/INT0
 *                 |     |
 *                 _     _
 *                 ^     ^  2 x 3.6V
 *                 |     |  zener to GND
 *                 |     |
 *                GND   GND
 */

/* The LED states */
#define LED_NUM     0x01
#define LED_CAPS    0x02
#define LED_SCROLL  0x04
#define LED_COMPOSE 0x08
#define LED_KANA    0x10


/* Originally used as a mask for the modifier bits, but now also
   used for other x -> 2^x conversions (lookup table). */
const char modmask[8] PROGMEM = {
    0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
  };


/* USB report descriptor (length is defined in usbconfig.h)
   This has been changed to conform to the USB keyboard boot
   protocol */
const char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH]
  PROGMEM = {
    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)
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
    0x95, 0x05,                    //   REPORT_COUNT (5)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x05, 0x08,                    //   USAGE_PAGE (LEDs)
    0x19, 0x01,                    //   USAGE_MINIMUM (Num Lock)
    0x29, 0x05,                    //   USAGE_MAXIMUM (Kana)
    0x91, 0x02,                    //   OUTPUT (Data,Var,Abs)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x03,                    //   REPORT_SIZE (3)
    0x91, 0x03,                    //   OUTPUT (Cnst,Var,Abs)
    0x95, 0x06,                    //   REPORT_COUNT (6)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x65,                    //   LOGICAL_MAXIMUM (101)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    0xc0                           // END_COLLECTION 
};

/* This buffer holds the last values of the scanned keyboard matrix */
static uchar bitbuf[NUMROWS]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};

/* The ReportBuffer contains the USB report sent to the PC */
static uchar reportBuffer[8];    /* buffer for HID reports */
static uchar idleRate;           /* in 4 ms units */
static uchar protocolVer=1;      /* 0 is the boot protocol, 1 is report protocol */

static void hardwareInit(void) {
#if 0
  PORTB = 0x3F;   /* Port B are row drivers */
  DDRB  = 0x00;   /* TODO: all pins output */

  PORTC = 0xff;   /* activate all pull-ups */
  DDRC  = 0x00;   /* all pins input */
 
  PORTD = 0xfa;   /* 1111 1010 bin: activate pull-ups except on USB lines */
  DDRD  = 0x07;   /* 0000 0111 bin: all pins input except USB (-> USB reset) */

  /* USB Reset by device only required on Watchdog Reset */
  _delay_us(11);   /* delay >10ms for USB reset */

  DDRD = 0x02;    /* 0000 0010 bin: remove USB reset condition */
  /* configure timer 0 for a rate of 12M/(1024 * 256) = 45.78 Hz (~22ms) */
#endif
  TCCR0 = 5;      /* timer 0 prescaler: 1024 */
}

/* Table for decoding bit positions of the last three rows */
const char extrows[3] PROGMEM = { 0x10, 0x20, 0x08 };


/* This function scans the entire keyboard, debounces the keys, and
   if a key change has been found, a new report is generated, and the
   function returns true to signal the transfer of the report. */
static uchar scankeys(void) {
  return 0; //no matrix connected
  uchar reportIndex=1; /* First available report entry is 2 */
  uchar retval=0;
  uchar row,data,key, modkeys;
  volatile uchar col, mask;
  static uchar debounce=5;

  for (row=0;row<NUMROWS;++row) { /* Scan all rows */
    DDRD&=~0b00111000; /* all 3 are input */
    PORTD|=0b00111000; /* pull-up enable */
    if (row<6) {
      data=pgm_read_byte(&modmask[row]);
      DDRB=data;
      PORTB=~data;
    } else { // 3 extra rows are on PORTD
      DDRB=0;
      PORTB=0xFF;
      data=pgm_read_byte(&extrows[row-6]);
      DDRD|=data;
      PORTD&=~data;
    }
   
    _delay_us(30); /* Used to be small loop, but the compiler optimized it away ;-) */
   
    data=(PINC&0x3F)|(PIND&0xC0);
    if (data^bitbuf[row]) {
      debounce=10; /* If a change was detected, activate debounce counter */
    }
    bitbuf[row]=data; /* Store the result */
  }

  if (debounce==1) { /* Debounce counter expired */
    modkeys=0;
    memset(reportBuffer,0,sizeof(reportBuffer)); /* Clear report buffer */
    for (row=0;row<NUMROWS;++row) { /* Process all rows for key-codes */
      data=bitbuf[row]; /* Restore buffer */
     
      if (data!=0xFF) { /* Anything on this row? - optimization */
        for (col=0,mask=1;col<8;++col,mask<<=1) { /* yes - check individual bits */
          if (!(data&mask)) { /* Key detected */
            key=pgm_read_byte(&keymap[row][col]); /* Read keyboard map */
            if (key>KEY_Special) { /* Special handling of shifted keys */
              /* Modifiers have not been decoded yet - handle manually */
              uchar keynum=key-(KEY_Special+1);
              if ((bitbuf[4]&0b01000000)&& /* Rshift */
                   ((bitbuf[7]&0b00000010)||(key>=SPC_crsrud))) {/* Lshift */
                key=pgm_read_byte(&spec_keys[keynum][0]); /* Unmodified */
                modkeys=pgm_read_byte(&spec_keys[keynum][1]);
              } else {
                key=pgm_read_byte(&spec_keys[keynum][2]); /* Shifted */
                modkeys=pgm_read_byte(&spec_keys[keynum][3]);
              }
            } else if (key>KEY_Modifiers) { /* Is this a modifier key? */
              reportBuffer[0]|=pgm_read_byte(&modmask[key-(KEY_Modifiers+1)]);
              key=0;
            }
            if (key) { /* Normal keycode should be added to report */
              if (++reportIndex>=sizeof(reportBuffer)) { /* Too many keycodes - rollOver */
                if (!retval&0x02) { /* Only fill buffer once */
                  memset(reportBuffer+2, KEY_errorRollOver, sizeof(reportBuffer)-2);
                  retval|=2; /* continue decoding to get modifiers */
                }
              } else {
                reportBuffer[reportIndex]=key; /* Set next available entry */
              }
            }
          }
        }
      }
    }
    if (modkeys&0x80) { /* Clear RSHIFT */
      reportBuffer[0]&=~0x20;
    }
    if (modkeys&0x08) { /* Clear LSHIFT */
      reportBuffer[0]&=~0x02;
    }
    reportBuffer[0]|=modkeys&0x77; /* Set other modifiers */

    retval|=1; /* Must have been a change at some point, since debounce is done */
  }
  if (debounce) debounce--; /* Count down, but avoid underflow */
  return retval;
}

uchar expectReport=0;
uchar LEDstate=0;

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 */
      return sizeof(reportBuffer);
    }else if(rq->bRequest == USBRQ_HID_SET_REPORT){
      if (rq->wLength.word == 1) { /* We expect one byte reports */
        expectReport=1;
        return 0xFF; /* Call usbFunctionWrite with data */
      } 
    }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 if(rq->bRequest == USBRQ_HID_GET_PROTOCOL) {
      if (rq->wValue.bytes[1] < 1) {
        protocolVer = rq->wValue.bytes[1];
      }
    }else if(rq->bRequest == USBRQ_HID_SET_PROTOCOL) {
      usbMsgPtr = &protocolVer;
      return 1;
    }
  }
  return 0;
}

uchar usbFunctionWrite(uchar *data, uchar len) {
  if ((expectReport)&&(len==1)) {
    LEDstate=data[0]; /* Get the state of all 5 LEDs */
    if (LEDstate&LED_CAPS) { /* Check state of CAPS lock LED */
      PORTD|=0x02;
    } else {
      PORTD&=~0x02;
    }
    expectReport=0;
    return 1;
  }
  expectReport=0;
  return 0x01;
}

int main(void) {
  uchar   updateNeeded = 0;
  uchar   idleCounter = 0;

  wdt_enable(WDTO_2S); /* Enable watchdog timer 2s */
  hardwareInit(); /* Initialize hardware (I/O) */
 
  odDebugInit();

  usbInit(); /* Initialize USB stack processing */
  sei(); /* Enable global interrupts */
 
  for(;;){  /* Main loop */
    wdt_reset(); /* Reset the watchdog */
    usbPoll(); /* Poll the USB stack */

    updateNeeded|=scankeys(); /* Scan the keyboard for changes */
   
    /* Check timer if we need periodic reports */
    if(TIFR & (1<<TOV0)){
      TIFR = 1<<TOV0; /* Reset flag */
      if(idleRate != 0){ /* Do we need periodic reports? */
        if(idleCounter > 4){ /* Yes, but not yet */
          idleCounter -= 5;   /* 22 ms in units of 4 ms */
        }else{ /* Yes, it is time now */
          updateNeeded = 1;
          idleCounter = idleRate;
        }
      }
    }
   
    /* If an update is needed, send the report */
    if(updateNeeded && usbInterruptIsReady()){
      updateNeeded = 0;
      usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
    }
  }
  return 0;
}



Usbconfig.h

Code: Select all

/* Name: usbconfig.h
 * Project: AVR USB driver
 * Author: Christian Starkjohann
 * Creation Date: 2005-04-01
 * 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 43 2005-04-10 21:04:36Z cs $
 */

#ifndef __usbconfig_h_included__
#define __usbconfig_h_included__

/*
General Description:
This file contains parts of the USB driver which can be configured and can or
must be adapted to your hardware.

Please note that the usbdrv contains a usbconfig-prototype.h file now. We
recommend that you use that file as a template because it will always list
the newest features and options.
*/

/* ---------------------------- Hardware Config ---------------------------- */

#define USB_CFG_IOPORTNAME      B
/* This is the port where the USB bus is connected. When you configure it to
 * "B", the registers PORTB, PINB and DDRB will be used.
 */
#define USB_CFG_DMINUS_BIT      0
/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected.
 * This may be any bit in the port.
 */
#define USB_CFG_DPLUS_BIT       1
/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected.
 * This may be any bit in the port. Please note that D+ must also be connected
 * to interrupt pin INT0!
 */
#define F_CPU       12000000UL
#define USB_CFG_CLOCK_KHZ       (F_CPU/1000)

/* ----------------------- Optional Hardware Config ------------------------ */

/* #define USB_CFG_PULLUP_IOPORTNAME   D */
/* If you connect the 1.5k pullup resistor from D- to a port pin instead of
 * V+, you can connect and disconnect the device from firmware by calling
 * the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h).
 * This constant defines the port on which the pullup resistor is connected.
 */
/* #define USB_CFG_PULLUP_BIT          4 */
/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined
 * above) where the 1.5k pullup resistor is connected. See description
 * above for details.
 */

/* --------------------------- Functional Range ---------------------------- */

#define USB_CFG_HAVE_INTRIN_ENDPOINT    1
/* Define this to 1 if you want to compile a version with two endpoints: The
 * default control endpoint 0 and an interrupt-in endpoint 1.
 */
#define USB_CFG_HAVE_INTRIN_ENDPOINT3   0
/* Define this to 1 if you want to compile a version with three endpoints: The
 * default control endpoint 0, an interrupt-in endpoint 1 and an interrupt-in
 * endpoint 3. You must also enable endpoint 1 above.
 */
#define USB_CFG_IMPLEMENT_HALT          0
/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature
 * for endpoint 1 (interrupt endpoint). Although you may not need this feature,
 * it is required by the standard. We have made it a config option because it
 * bloats the code considerably.
 */
#define USB_CFG_INTR_POLL_INTERVAL      10
/* If you compile a version with endpoint 1 (interrupt-in), this is the poll
 * interval. The value is in milliseconds and must not be less than 10 ms for
 * low speed devices.
 */
#define USB_CFG_IS_SELF_POWERED         1
/* Define this to 1 if the device has its own power supply. Set it to 0 if the
 * device is powered from the USB bus.
 */
#define USB_CFG_MAX_BUS_POWER           100
/* Set this variable to the maximum USB bus power consumption of your device.
 * The value is in milliamperes. [It will be divided by two since USB
 * communicates power requirements in units of 2 mA.]
 */
#define USB_CFG_IMPLEMENT_FN_WRITE      1
/* Set this to 1 if you want usbFunctionWrite() to be called for control-out
 * transfers. Set it to 0 if you don't need it and want to save a couple of
 * bytes.
 */
#define USB_CFG_IMPLEMENT_FN_READ       0
/* Set this to 1 if you need to send control replies which are generated
 * "on the fly" when usbFunctionRead() is called. If you only want to send
 * data from a static buffer, set it to 0 and return the data from
 * usbFunctionSetup(). This saves a couple of bytes.
 */
#define USB_CFG_IMPLEMENT_FN_WRITEOUT   0
/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoint 1.
 * You must implement the function usbFunctionWriteOut() which receives all
 * interrupt/bulk data sent to endpoint 1.
 */
#define USB_CFG_HAVE_FLOWCONTROL        0
/* Define this to 1 if you want flowcontrol over USB data. See the definition
 * of the macros usbDisableAllRequests() and usbEnableAllRequests() in
 * usbdrv.h.
 */

/* -------------------------- Device Description --------------------------- */

/* We cannot use Obdev's free shared VID/PID pair because this is a HID.
 * We use John Hyde's VID (author of the book "USB Design By Example") for
 * this example instead. John has offered this VID for use by students for
 * non-commercial devices. Well... This example is for demonstration and
 * education only... DO NOT LET DEVICES WITH THIS VID ESCAPE YOUR LAB!
 * The Product-ID is a random number.
 */
#define  USB_CFG_VENDOR_ID  0x42, 0x42
/* USB vendor ID for the device, low byte first. If you have registered your
 * own Vendor ID, define it here. Otherwise you use obdev's free shared
 * VID/PID pair. Be sure to read USBID-License.txt for rules!
 */
#define  USB_CFG_DEVICE_ID  0xbe, 0xba
/* This is the ID of the product, low byte first. It is interpreted in the
 * scope of the vendor ID. If you have registered your own VID with usb.org
 * or if you have licensed a PID from somebody else, define it here. Otherwise
 * you use obdev's free shared VID/PID pair. Be sure to read the rules in
 * USBID-License.txt!
 */
#define USB_CFG_DEVICE_VERSION  0x98, 0x01
/* Version number of the device: Minor number first, then major number.
 */
#define USB_CFG_VENDOR_NAME     'S','p','a','c','e','m','a','n',' ',\
                                'S','p','i','f','f',' ','h','t','t','p',\
                                ':','/','/','s','y','m','l','i','n','k','.','d','k'
#define USB_CFG_VENDOR_NAME_LEN 32
/* These two values define the vendor name returned by the USB device. The name
 * must be given as a list of characters under single quotes. The characters
 * are interpreted as Unicode (UTF-16) entities.
 * If you don't want a vendor name string, undefine these macros.
 * ALWAYS define a vendor name containing your Internet domain name if you use
 * obdev's free shared VID/PID pair. See the file USBID-License.txt for
 * details.
 */
#define USB_CFG_DEVICE_NAME     'S','p','i','f','f','\'','s',' ','C','6',\
                                '4',' ','K','e','y','b','o','a','r','d'
#define USB_CFG_DEVICE_NAME_LEN 20
/* Same as above for the device name. If you don't want a device name, undefine
 * the macros. See the file USBID-License.txt before you assign a name.
 */
#define USB_CFG_SERIAL_NUMBER   '4', '2'
#define USB_CFG_SERIAL_NUMBER_LEN   2
/* Same as above for the serial number. If you don't want a serial number,
 * undefine the macros.
 * It may be useful to provide the serial number through other means than at
 * compile time. See the section about descriptor properties below for how
 * to fine tune control over USB descriptors such as the string descriptor
 * for the serial number.
 */
#define USB_CFG_DEVICE_CLASS    0
#define USB_CFG_DEVICE_SUBCLASS 0
/* See USB specification if you want to conform to an existing device class.
 */
#define USB_CFG_INTERFACE_CLASS     0x03    /* HID class */
#define USB_CFG_INTERFACE_SUBCLASS  0x01    /* Boot-device subclass */
#define USB_CFG_INTERFACE_PROTOCOL  0x01    /* Keyboard protocol */
/* See USB specification if you want to conform to an existing device class or
 * protocol.
 */
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH    63   /* total length of report descriptor */
/* Define this to the length of the HID report descriptor, if you implement
 * an HID device. Otherwise don't define it or define it to 0.
 */

/* ------------------- Fine Control over USB Descriptors ------------------- */
/* If you don't want to use the driver's default USB descriptors, you can
 * provide our own. These can be provided as (1) fixed length static data in
 * flash memory, (2) fixed length static data in RAM or (3) dynamically at
 * runtime in the function usbFunctionDescriptor(). See usbdrv.h for more
 * information about this function.
 * Descriptor handling is configured through the descriptor's properties. If
 * no properties are defined or if they are 0, the default descriptor is used.
 * Possible properties are:
 *   + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched
 *     at runtime via usbFunctionDescriptor().
 *   + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found
 *     in static memory is in RAM, not in flash memory.
 *   + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash),
 *     the driver must know the descriptor's length. The descriptor itself is
 *     found at the address of a well known identifier (see below).
 * List of static descriptor names (must be declared PROGMEM if in flash):
 *   char usbDescriptorDevice[];
 *   char usbDescriptorConfiguration[];
 *   char usbDescriptorHidReport[];
 *   char usbDescriptorString0[];
 *   int usbDescriptorStringVendor[];
 *   int usbDescriptorStringDevice[];
 *   int usbDescriptorStringSerialNumber[];
 * Other descriptors can't be provided statically, they must be provided
 * dynamically at runtime.
 *
 * Descriptor properties are or-ed or added together, e.g.:
 * #define USB_CFG_DESCR_PROPS_DEVICE   (USB_PROP_IS_RAM | USB_PROP_LENGTH(18))
 *
 * The following descriptors are defined:
 *   USB_CFG_DESCR_PROPS_DEVICE
 *   USB_CFG_DESCR_PROPS_CONFIGURATION
 *   USB_CFG_DESCR_PROPS_STRINGS
 *   USB_CFG_DESCR_PROPS_STRING_0
 *   USB_CFG_DESCR_PROPS_STRING_VENDOR
 *   USB_CFG_DESCR_PROPS_STRING_PRODUCT
 *   USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER
 *   USB_CFG_DESCR_PROPS_HID
 *   USB_CFG_DESCR_PROPS_HID_REPORT
 *   USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver)
 *
 */

#define USB_CFG_DESCR_PROPS_DEVICE                  0
#define USB_CFG_DESCR_PROPS_CONFIGURATION           0
#define USB_CFG_DESCR_PROPS_STRINGS                 0
#define USB_CFG_DESCR_PROPS_STRING_0                0
#define USB_CFG_DESCR_PROPS_STRING_VENDOR           0
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT          0
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER    0
#define USB_CFG_DESCR_PROPS_HID                     0
#define USB_CFG_DESCR_PROPS_HID_REPORT              0
#define USB_CFG_DESCR_PROPS_UNKNOWN                 0

/* ----------------------- Optional MCU Description ------------------------ */

/* The following configurations have working defaults in usbdrv.h. You
 * usually don't need to set them explicitly. Only if you want to run
 * the driver on a device which is not yet supported or with a compiler
 * which is not fully supported (such as IAR C) or if you use a differnt
 * interrupt than INT0, you may have to define some of these.
 */
/* #define USB_INTR_CFG            MCUCR */
/* #define USB_INTR_CFG_SET        ((1 << ISC00) | (1 << ISC01)) */
/* #define USB_INTR_CFG_CLR        0 */
/* #define USB_INTR_ENABLE         GIMSK */
/* #define USB_INTR_ENABLE_BIT     INT0 */
/* #define USB_INTR_PENDING        GIFR */
/* #define USB_INTR_PENDING_BIT    INTF0 */

#endif /* __usbconfig_h_included__ */

blargg
Rank 3
Rank 3
Posts: 102
Joined: Thu Nov 14, 2013 10:01 pm

Re: Difficulties Replicating C64 USB

Post by blargg » Thu Aug 28, 2014 2:04 am

My diff will only work on a USBasp, which has USB on PB0, PB1, and PD2 (for INT0). If you're going to try it on your hardware you'll need to leave the USB pins at what they were before. All the other changes I made should work on your hardware. The point of my test was just to verify that the code basically works on similar hardware.

Lostchemist
Posts: 10
Joined: Wed Aug 27, 2014 1:16 am

Re: Difficulties Replicating C64 USB

Post by Lostchemist » Thu Aug 28, 2014 3:27 am

Alright. I gave up for the day, moved on to different projects. Clearly I'm new at this, I apologize if I keep missing the obvious.

I find it odd though that if I changed the usb pins, the usbtiny still reported that the upload was accurate.

blargg
Rank 3
Rank 3
Posts: 102
Joined: Thu Nov 14, 2013 10:01 pm

Re: Difficulties Replicating C64 USB

Post by blargg » Thu Aug 28, 2014 4:21 am

Sorry if I came across as unpleasant. I know you said you give up, but I think you probably did the changes right in your earlier message and showed pretty well that it's probably hardware. If you take some sharp photos of the front and back of the board, I'd be happy to look over them to see any problems. It sounds like you're close to having it work. Have you run a basic blink test to be sure you've got the crystal configured correctly? This should flash an LED on PD1 at one flash per second (on the schematic this connects to the C64's keyboard LED):

Code: Select all

#if 0
avr-gcc -mmcu=atmega8 -DF_CPU=12000000UL -Wall -Os -o blink.elf blink.c
avr-objcopy -j .text -j .data -O ihex blink.elf blink.hex
avrdude -p atmega8 -P com1 -c usbtiny -U blink.hex
exit
#endif

#include <avr/io.h>
#include <util/delay.h>

int main( void )
{
   DDRD |= 1<<1;
   for ( ;; )
   {
      PORTD ^= 1<<1;
      _delay_ms(500);
   }
}

Lostchemist
Posts: 10
Joined: Wed Aug 27, 2014 1:16 am

Re: Difficulties Replicating C64 USB

Post by Lostchemist » Thu Aug 28, 2014 5:55 am

Oh, you weren't unpleasant. I'm only giving up for the evening. I just had other projects that needed attention, and all of my stuff is in my lab on campus. I am using this project as a learning experience for a set of chemical sensing devices I'm designing.

I can take pictures of the boards I made tomorrow. Right now, I have this...

The schematic: Image


The board: Image


Partslist

Code: Select all

Part         Value              Package      Library  Position (inch)       Orientation

ATMEGA8-PU16 ATMEGA8-PU         DIL28-3      atmega8  (1.15 1.5)            R180
C1           22pF               C025-024X044 resistor (0.9 1.9)             R0
C2           22pF               C025-024X044 resistor (1.8 1.9)             R0
C3           0.1uF              C025-024X044 resistor (1.45 1.05)           R270
C5           10uF               E2,5-5       resistor (1.15 0.15)           R270
C6           0.1uF              C025-024X044 resistor (1.75 1.05)           R270
C64KBD                          MA20-1       con-lstb (0.25 1)              R90
C64LED                          MA03-1       con-lstb (0.75 0.25)           R0
D1           1N4728             DO41Z10      diode    (1.65 0.4)            R0
D2           1N4728             DO41Z10      diode    (1.65 0.6)            R0
H1           MOUNT-PAD-ROUND2.8 2,8-PAD      holes    (1.1 0.65)            R0
ICSP                            MA03-2       con-lstb (1.05 1.05)           R0
Q1           12.000Mhz          QS           special  (1.35 1.85)           R0
R1           82R                0204V        resistor (1.8 0.8)             R180
R2           82R                0204V        resistor (1.3 0.4)             R0
R3           470R               0204V        resistor (1.5 1.2)             R0
R4           2k2R               0204V        resistor (1.5 0.8)             R180
R5           4k7R               0204V        resistor (0.6 1.9)             R180
USB                             PN61729      con-berg (1.6 0.05)            R0


Due to an error on my part, the USB pins are connected by patch wires rather than printed wires.

blargg
Rank 3
Rank 3
Posts: 102
Joined: Thu Nov 14, 2013 10:01 pm

Re: Difficulties Replicating C64 USB

Post by blargg » Thu Aug 28, 2014 7:16 am

First thing I notice that's possibly a problem is the 1W zener diodes. I've read of people having trouble with these due to their capacitance, and recommendations to use 0.5W. If you don't have any handy, you could build a temporary alternate level converter with 6 generic signal diodes just to see whether the rest of the circuit is OK.

Lostchemist
Posts: 10
Joined: Wed Aug 27, 2014 1:16 am

Re: Difficulties Replicating C64 USB

Post by Lostchemist » Thu Aug 28, 2014 10:05 pm

So far today, I reverted the bits for the USB pins. Is your programmer going directly through the USB? I don't quite understand your setup if you changed those.

I also did some work with the diodes. The voltage drop across D+ was 2.134V, across D- was 0V (resistor troubles). I fixed the resistor and tried the series diodes you suggested in the other thread. I've got 6 1n914 in total and in a similar arrangement. The D+ to ground drop is now 3.567V and at D- to ground is 3.805V. It seems like those should be closer to each other. I may have to break out the osc. to look at the signal.

It seems R5 is reading 4k instead of 4k7, so I need to take a look at that too.
EDIT: Nevermind, the ground path just seems to be 4k. The 4k7 wasn't a problem.

blargg
Rank 3
Rank 3
Posts: 102
Joined: Thu Nov 14, 2013 10:01 pm

Re: Difficulties Replicating C64 USB

Post by blargg » Thu Aug 28, 2014 10:45 pm

Is your programmer going directly through the USB? I don't quite understand your setup if you changed those.


Here's the relevant portion of the schematic of a USBasp that I ran the code on. USB is on PB0 and PB1, with PB1 also tied to INT0.

Image

Lostchemist
Posts: 10
Joined: Wed Aug 27, 2014 1:16 am

Re: Difficulties Replicating C64 USB

Post by Lostchemist » Fri Aug 29, 2014 12:54 am

Success. The computer is now recognizing that it is, in fact, a device. This occurred through a series of steps.

1. Tried to blink. Failed. No response.

2. PI suggested that the diodes were pretty much pointless for a device with such low power. I should remove the diodes.

3. Tried to blink. Partial success. The blinking had a total cycle time of exactly 12 seconds.

4. Obvious solution -- Rewrite fuses. (It is possible that the makefile was trying to give the proper fuses, but the device couldn't communicate due to the diode capacitance. Manual was much more straightforward though.)

5. Blink. Success.

6. Program, also apparent success. I'm wondering if I need to change the #define USB_CFG_IS_SELF_POWERED line to 0, since I'm not working with an externally powered breadboard.


If I have total success, or more issues, I will report back.

Lostchemist
Posts: 10
Joined: Wed Aug 27, 2014 1:16 am

Re: Difficulties Replicating C64 USB

Post by Lostchemist » Mon Sep 01, 2014 2:57 am

My computers now acknowledge that the device exists, at least in part. The keyboard is not usable though.

Plugging into Windows results is two possible outcomes. If the keyboard is attached to the circuit when plugged in, the device sometimes gives error 43. If the keyboard is not attached to the surface, the device always is recognized as HID Keyboard. The device name and vendor name are omitted. I tried changing the VID/PID pair to force Windows to use new entries (rather than dealing with registry hacks), but this did not change anything. I assume if the report descriptors are correct in the usbconfig, then I should see "SpacemanSpiff" instead of "(standard keyboard)".

Plugging into the raspi, I can only get this. I tried to sudo bash a forced rebind of the usb device, but I still have incomplete information on the report descriptor. I would guess that this is the source of my problems with identification. Anyone have any thoughts?

Code: Select all

lsusb -vd f055:000f

Bus 001 Device 016: ID f055:000f
Couldn't open device, some information will be missing
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0         8
  idVendor           0xf055
  idProduct          0x000f
  bcdDevice            1.00
  iManufacturer           1
  iProduct                2
  iSerial                 0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           34
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      1 Boot Interface Subclass
      bInterfaceProtocol      1 Keyboard
      iInterface              0
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.01
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      63
         Report Descriptors:
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval              10


I think I should tackle this identification issue before I try to determine why the keyboard isn't sending keystrokes. I have confirmed that the keyboard PCB is fine, and there is decent conductivity in the wires.


Also, since blargg asked for pictures, I took some of the two versions I have made of this circuit.

Obverse
Image

Reverse
Image

Lostchemist
Posts: 10
Joined: Wed Aug 27, 2014 1:16 am

Re: Difficulties Replicating C64 USB

Post by Lostchemist » Tue Sep 02, 2014 3:07 am

Another interesting observation.

When the board is powered through the tinyusb programmer, the C64LED is always on. When the board is powered through the USB connector, C64LED stays lit for 1.52 s (average of three tests with a stopwatch), then turns off until re-plugged. The LED is powered by the UART TX in the PD1 pin. Is this a useful symptom of my problem? The main.c file checks the led state at the start, then ignores it. Old versions of the code apparently used PD1 as a UART debugging pin. Has there been some change in the VUSB code since ca. Jan-2008 that might cause some interference here?

Post Reply