From 8714ef1768f7f29caf64c05159e7c6bc6aee18bf Mon Sep 17 00:00:00 2001 From: Ole Artz Date: Mon, 30 May 2022 15:44:37 +0200 Subject: [PATCH] DCDC MDC Converter Board: add comments to the working code and remove >dummy< from readme file. --- atmega32u4/dcdc_mdc/Makefile | 230 ++++++++ atmega32u4/dcdc_mdc/README.md | 75 +++ atmega32u4/dcdc_mdc/dcdc.pl | 334 +++++++++++ atmega32u4/dcdc_mdc/main.c | 674 ++++++++++++++++++++++ atmega32u4/dcdc_mdc/usb_serial.c | 937 +++++++++++++++++++++++++++++++ atmega32u4/dcdc_mdc/usb_serial.h | 124 ++++ 6 files changed, 2374 insertions(+) create mode 100644 atmega32u4/dcdc_mdc/Makefile create mode 100644 atmega32u4/dcdc_mdc/README.md create mode 100755 atmega32u4/dcdc_mdc/dcdc.pl create mode 100644 atmega32u4/dcdc_mdc/main.c create mode 100644 atmega32u4/dcdc_mdc/usb_serial.c create mode 100644 atmega32u4/dcdc_mdc/usb_serial.h diff --git a/atmega32u4/dcdc_mdc/Makefile b/atmega32u4/dcdc_mdc/Makefile new file mode 100644 index 0000000..8ee15d8 --- /dev/null +++ b/atmega32u4/dcdc_mdc/Makefile @@ -0,0 +1,230 @@ +# Hey Emacs, this is a -*- makefile -*- + +# AVR-GCC Makefile template, derived from the WinAVR template (which +# is public domain), believed to be neutral to any flavor of "make" +# (GNU make, BSD make, SysV make) + + +MCU = atmega32u4 +FORMAT = ihex +TARGET = main +SRC = $(TARGET).c usb_serial.c +ASRC = +OPT = 2 +PORT=/dev/ttyACM0 + +# Name of this Makefile (used for "make depend"). +MAKEFILE = Makefile + +# Debugging format. +# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. +# AVR (extended) COFF requires stabs, plus an avr-objcopy run. +DEBUG = stabs + +# 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=8000000 + +# Place -I options here +CINCS = + + +CDEBUG = -g$(DEBUG) +CWARN = -Wall -Wstrict-prototypes +CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wl,--relax +#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) +CFLAGS = $(CDEBUG) $(CDEFS) $(CINCS) -O$(OPT) $(CWARN) $(CSTANDARD) $(CEXTRA) $(CTUNING) -DSTR_SERIAL_NUMBER=L\"$(NUMBER)\" + + +#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs + + +#Additional libraries. + +# 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 + +PRINTF_LIB = + +# 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 + +SCANF_LIB = + +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,--section-start,.data=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 = + +#LDMAP = $(LDFLAGS) -Wl,-Map=$(TARGET).map,--cref +LDFLAGS = $(EXTMEMOPTS) $(LDMAP) $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB) + + +# Programming support using avrdude. Settings and variables. + +AVRDUDE_PROGRAMMER = dragon_jtag +AVRDUDE_PORT = usb + +AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex +#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep + + +# 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 +# to submit bug reports. +#AVRDUDE_VERBOSE = -v -v + +AVRDUDE_BASIC = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) +AVRDUDE_FLAGS = $(AVRDUDE_BASIC) $(AVRDUDE_NO_VERIFY) $(AVRDUDE_VERBOSE) $(AVRDUDE_ERASE_COUNTER) + + +CC = avr-gcc +OBJCOPY = avr-objcopy +OBJDUMP = avr-objdump +SIZE = avr-size +NM = avr-nm +AVRDUDE = avrdude +REMOVE = rm -f +MV = mv -f + +# Define all object files. +OBJ = $(SRC:.c=.o) $(ASRC:.S=.o) + +# Define all listing files. +LST = $(ASRC:.S=.lst) $(SRC:.c=.lst) + +# Combine all necessary flags and optional flags. +# Add target processor to flags. +ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) +ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS) + + +# Default target. +all: build + +build: elf hex eep + +elf: $(TARGET).elf +hex: $(TARGET).hex +eep: $(TARGET).eep +lss: $(TARGET).lss +sym: $(TARGET).sym + + +# Program the device. +program: $(TARGET).hex $(TARGET).eep + $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) $(AVRDUDE_WRITE_EEPROM) + + +size: + $(SIZE) -C --mcu=$(MCU) $(TARGET).elf + +# 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 + $(COFFCONVERT) -O coff-avr $(TARGET).elf $(TARGET).cof + + +extcoff: $(TARGET).elf + $(COFFCONVERT) -O coff-ext-avr $(TARGET).elf $(TARGET).cof + + +.SUFFIXES: .elf .hex .eep .lss .sym + +.elf.hex: + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +.elf.eep: + -$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --change-section-lma .eeprom=0 -O $(FORMAT) $< $@ + +# Create extended listing file from ELF output file. +.elf.lss: + $(OBJDUMP) -h -S $< > $@ + +# Create a symbol table from ELF output file. +.elf.sym: + $(NM) -n $< > $@ + + + +# Link: create ELF output file from object files. +$(TARGET).elf: $(OBJ) + $(CC) $(ALL_CFLAGS) $(OBJ) --output $@ $(LDFLAGS) + + +# Compile: create object files from C source files. +.c.o: + $(CC) -c $(ALL_CFLAGS) $< -o $@ + + +# Compile: create assembler files from C source files. +.c.s: + $(CC) -S $(ALL_CFLAGS) $< -o $@ + + +# Assemble: create object files from assembler source files. +.S.o: + $(CC) -c $(ALL_ASFLAGS) $< -o $@ + + +# Target: clean project. +clean: + $(REMOVE) $(TARGET).hex $(TARGET).eep $(TARGET).cof $(TARGET).elf \ + $(TARGET).map $(TARGET).sym $(TARGET).lss \ + $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) + +depend: + if grep '^# DO NOT DELETE' $(MAKEFILE) >/dev/null; \ + then \ + sed -e '/^# DO NOT DELETE/,$$d' $(MAKEFILE) > \ + $(MAKEFILE).$$$$ && \ + $(MV) $(MAKEFILE).$$$$ $(MAKEFILE); \ + fi + echo '# DO NOT DELETE THIS LINE -- make depend depends on it.' \ + >> $(MAKEFILE); \ + $(CC) -M -mmcu=$(MCU) $(CDEFS) $(CINCS) $(SRC) $(ASRC) >> $(MAKEFILE) + +.PHONY: all build elf hex eep lss sym program coff extcoff clean depend program_bootloader program_arduino + +program_bootloader: all + dfu-programmer $(MCU) erase && dfu-programmer $(MCU) flash $(TARGET).hex && dfu-programmer $(MCU) start + +program_arduino: all + avrdude -patmega32u4 -cavr109 -P$(PORT) -b57600 -D -Uflash:w:$(TARGET).hex:i diff --git a/atmega32u4/dcdc_mdc/README.md b/atmega32u4/dcdc_mdc/README.md new file mode 100644 index 0000000..79e50e5 --- /dev/null +++ b/atmega32u4/dcdc_mdc/README.md @@ -0,0 +1,75 @@ +# MDC DCDC Converter Board + +This is the firmware for the DCDC MDC Converter Board which serves as the power supply for the MDC layers/chambers. + +------------------------------------------------------------------------------------------------------------------------ + +## Installation + +1. make +2. connect the Board via MicroUSB +3. RESET the Microcontroller +4. dfu-programmer atmega32u4 erase --force +5. make program_bootloader + +------------------------------------------------------------------------------------------------------------------------ + +## Protocol definition + +Data format: XuuGcRvvvv (10 characters) + +The data format has to end with \n + +| Value | Description | +|---------|---------------------------------------------------------------------------------| +| X | command (W := write, R := read, A := answer, etc.) | +| uu | Controllernumber (HEX value) | +| G | Groupnumber (to talk to all channels, that belong together in one command) | +| c | Channelnumber in the group (HEX value) | +| R | register (HEX value) | +| vvvv | 16 Bit value | + +Example: "RF2012FE51\n" + +------------------------------------------------------------------------------------------------------------------------ + +## Groupnumber definition + +Each group has two channels (0, 1) + +| Groupnumber | Description | +|---------------|---------------| +| 0 | DCDC 0 | +| 1 | DCDC 1 | +| 2 | DCDC 2 | +| 3 | DCDC 3 | + + +------------------------------------------------------------------------------------------------------------------------ + +## Register definition + +| Registers | Description | +|---------------|---------------------------------------------------------------| +| 0 | DCDC (Group) ON/OFF | +| 1 | DCDC (Channel) set voltage adjustment resistors | +| 2 | Voltage V_in (RO) | +| 3 | Current C_in (RO) | +| 4 | Temperature (DCDC 2/Group 3) (RO) | +| 5 | [15:4] Firmware; [3:2] reserved; [1] Switch ; [0] LED (RO) | +| 6 | Current Offset | +| 7 | Voltage V_out (RO) | +| 8 | Current C_out (NA) | +| 9 | Sense GND (NA) | + +NA := not available +RO := read only + +------------------------------------------------------------------------------------------------------------------------ + +## Usage + +The DCDC MDC Converter Board get his operating voltage via MicroUSB and receives messages via the LANTelnetServerBoard with UART Baud rate 57600. + +------------------------------------------------------------------------------------------------------------------------ +### Version 1.1, 2022-05-30 diff --git a/atmega32u4/dcdc_mdc/dcdc.pl b/atmega32u4/dcdc_mdc/dcdc.pl new file mode 100755 index 0000000..577baa1 --- /dev/null +++ b/atmega32u4/dcdc_mdc/dcdc.pl @@ -0,0 +1,334 @@ +#!/usr/bin/perl +# if ($ENV{'SERVER_SOFTWARE'} =~ /HTTPi/i) { +# print "HTTP/1.0 200 OK\n"; +# print "Content-type: text/html\r\n\r\n"; +# } +# else { +# use lib '..'; +# print "Content-type: text/html\n\n"; +# } + +use strict; +use warnings; +use Device::SerialPort; +use IO::Socket; +use IO::Handle; +use Fcntl; + +use feature 'state'; +use URI::Escape; +use Data::Dumper; +use Time::HiRes qw( usleep); +use Getopt::Long; + +my $port; +my $help; +my $ser_dev; +my $isTrbNet = 0; +Getopt::Long::Configure(qw(gnu_getopt pass_through)); +GetOptions( + 'help|h' => \$help, + 'device|d=s' => \$ser_dev, + ) ; + +my $isEthernet = 0; +my $mode = 0; +my $ch; +my $uC; +my $reg; +my $rw; +my $val; +my $num = 0; +my $args = scalar @ARGV; + +# my $envstring = $ENV{'QUERY_STRING'}; +# +# +# my @new_command = split('&',$envstring); +# my $ser_dev = shift(@new_command); + +$ser_dev = "/dev/ttyUSB0" unless defined $ser_dev; + +sub PrintAnswer { + my ($s) = @_; + + print $s."\n"; + +} + +sub PrintAnswerNice { + my ($s) = @_; + my $FSR = 0.002; #Full Sclae Range is set to +-4.096V; LSB Size is 2mV + if (substr($s,0,1) ne 'A') {die 'not a correct Answer from DCDC board'} + my $command = hex(substr($s,5,1)); + my $ch = hex(substr($s,4,1)); + my $uC = hex(substr($s,1,2)); + my $answ = hex(substr($s,6)); + print $s."\n"; + print "-----------------------------------------------\n"; + print "Board: ".$uC."\t"; + print "Channel: ".$ch."\n"; + print "-----------------------------------------------\n"; + + if ($command == 1) { # resistor adjustment values + print "Resistors active: "; + print "SEL0 : ".(($answ>> 0)&0xF)."\n"; + print "\t\t SEL1 : ".(($answ>> 4)&0xF)."\n"; + print "\t\t SEL2 : ".(($answ>> 8)&0xF)."\n"; + print "\t\t SEL3 : ".(($answ>>12)&0xF)."\n"; + } + if ($command == 2) { # Voltage + $answ = $answ >> 4; + my $calc = (($answ&0x7FF)-(($answ>>11)&0x1)*2048)*$FSR*11;# *11 to calculate real input value from voltage divider + printf "measured Voltage @ Input : %.3f Volt (RAW: 0x%x)\n", ($calc) , ($answ); + } + if ($command == 3) { # Current + $answ = $answ >> 4; + my $calc = (($answ&0x7FF)-(($answ>>11)&0x1)*2048)*$FSR*2; # 500mV/A -> Thats why multiplied by 2 + printf "measured Current @ Input : %.3f Ampere (RAW: 0x%x)\n", ($calc) , ($answ); + } + if ($command == 4) { # Temperature + $answ = $answ >> 4; + my $calc = (($answ&0x7FF)-(($answ>>11)&0x1)*2048)*0.125; + printf "measured temperature : %.2f°C (RAW: 0x%x)\n", ($calc) , ($answ); + } + if ($command == 5) { #Infos + print "Firmware : ".($answ>>4)."\n"; + print "SEL status : ".(($answ>>1) & 0x1)."\n"; + print "LED status : ".($answ & 0x1)."\n"; + } + if ($command == 6) { #Current Offset + $answ = $answ >> 4; + my $calc = (($answ&0x7F)-(($answ>>7)&0x1)*128)*$FSR*2; # 500mV/A -> Thats why multiplied by 2 + printf "set Current offset : %.3f Ampere (RAW: 0x%x)\n", ($calc) , ($answ); + } + + + print "\n"; + +} + +sub SendCmdShort { + my ($reg) = @_; + + if($args == 3 || $args == 4) { + # microcontroller number in chain + my $uC = $ARGV[1]; + if (substr($uC,0,2) eq "0x") {$uC = hex(substr($uC,2));} + if ($uC >= $num) { + die "This microcontroller number is not allowed! \n"; + } + + # channel number + my $ch = $ARGV[2]; + if (substr($ch,0,2) eq "0x") {$ch = hex(substr($ch,2));} + if ($ch >= 4) { + die "This channel does not exist\n"; + } + my $rw = "R"; + my $val = 0x0000; + my $cmd = sprintf("%s%02x0%01x%01x%04x",$rw,$uC,$ch,$reg,$val&0xFFFF); + #print $cmd."\n"; + PrintAnswerNice(Cmd($cmd)) #Answer without \n + } else { + die "Not all arguments were specified!\n"; + } + +} + +sub Cmd { + my ($c) = @_; + $c .= "\n"; + if($isEthernet == 0) { + for my $i (0..0) { + $port->write($c); + my $a = ""; + for my $j (0..16) { + my ($l,$s) = $port->read(12); + $a .= $l; + if ($l < 1) {next;} + #print "DBG".(length $s)."\n"; + #print $l." ".$s."\n"; + #if ($s =~ /^\w[a-f0-9]{9}\n/) { return substr($s,0,10);} + if ($s =~ /^\w[a-f0-9]{0,11}/) { my $size = length $s; $s = substr($s,0,$size-1); return $s;} + usleep(10000); + } + usleep(50000); + #print '.'; + } + } + if($isEthernet == 1) { + print $port "$c"; + my $x = ""; + for my $i (0..10) { + $x .= <$port>; + if($x && ($x =~ /\n/ || $x =~ /\r/) ) { + chomp $x; + return $x; + } + usleep(1000); + } + } + return ; +} + +if ($help || (defined $ARGV[0] && $ARGV[0] =~ /help/)) { + print "dcdc.pl [-d DEVICE] reg [uC [CHANNEL [OPERATION [REGISTER [VALUE]]]]]\n"; + print "dcdc.pl [-d DEVICE] CMD [uC [CHANNEL [VALUE]]]\n\n"; + print "DEVICE: Either a serial device or an IP number.\n"; + print "CMD: s: set DCDC setting, g: get DCDC setting,\n"; + print " t: temperature, v: input voltage, c: input current;\n"; + print "uC: number of Microcontrolle rin chain. As Hex or Dec\n"; + print "CHANNEL: Channel number, hex or decimal\n"; + print "OPERATION: R = Read; W = write\n"; + print "REGISTER: Register to access (dec or hex)\n"; + print "VALUE: A 16 Bit value, 4 hex digits or decimal\n"; + exit; +} + +if ($ser_dev =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { + $isEthernet = 1; + $port = IO::Socket::INET->new(PeerAddr => $ser_dev, PeerPort => 2323, Proto => "tcp", Type => SOCK_STREAM, Timeout => 1) + or (print("Device not found") && return); + } +else { + $port = new Device::SerialPort($ser_dev); + unless ($port) { + print "can't open serial interface $ser_dev\n"; + exit; + } + $port->user_msg('ON'); + $port->baudrate(57600); + $port->parity("none"); + $port->databits(8); + $port->stopbits(1); + $port->handshake("none"); + $port->read_char_time(0); + $port->read_const_time(50); + $port->write_settings; + } + +#Scan for number of Microcontrolles. +$num = Cmd("S"); +$num = hex(substr($num,1)); +if ($num == 0){ die "Can not access a power switch\n";} + + + +# microcontroller number in chain +if($args > 1) { + if ($ARGV[0] eq "reg") {$mode = 1;} + if ($ARGV[0] eq "s") {$mode = 2;} + if ($ARGV[0] eq "g") {$mode = 3;} + if ($ARGV[0] eq "t") {$mode = 4;} + if ($ARGV[0] eq "v") {$mode = 5;} + if ($ARGV[0] eq "c") {$mode = 6;} + if ($ARGV[0] eq "i") {$mode = 7;} +} + +if ($mode == 1) { + + # direct register access + if($args == 6 || $args == 5) { + + # microcontroller number in chain + $uC = $ARGV[1]; + if (substr($uC,0,2) eq "0x") {$uC = hex(substr($uC,2));} + if ($uC >= $num) { + die "This microcontroller Number is not allowed! \n"; + } + + # channel number + $ch = $ARGV[2]; + if (substr($ch,0,2) eq "0x") {$ch = hex(substr($ch,2));} + if ($ch >= 4) { + die "This channel does not exist\n"; + } + + # operation R/W + $rw = $ARGV[3]; + if ($rw eq "r" || $rw eq "R") { + $rw = "R"; + } elsif ($rw eq "w" || $rw eq "W") { + $rw = "W"; + if ($args == 5) { + die "Not all arguements were specified!\n"; + } + } else { + die "This register does not exist\n"; + } + + # Register + $reg = $ARGV[4]; + if (substr($reg,0,2) eq "0x") {$reg= hex(substr($reg,2));} + if ($reg >= 7) { + die "This register does not exist\n"; + } + + # Value + if ($rw eq "R") { + $val = 0x0; + } else { + $val = $ARGV[5]; + } + if (substr($val,0,2) eq "0x") {$val= hex(substr($val,2));} + if ($val > 0xFFFF) { + die "The value is greater than 16 bit! Only 16 bit are allowed\n"; + } + if($reg == 6) {$val *= 16;} + + my $cmd = sprintf("%s%02x0%01x%01x%04x",$rw,$uC,$ch,$reg,$val&0xffff); + print $cmd."\n"; + PrintAnswerNice(Cmd($cmd)) #Answer without \n + } else { + + die "Not all arguements were specified!\n"; + } +} + +# Set settings of DCDC +if($mode == 2) { + if($args == 4) { + # microcontroller number in chain + $uC = $ARGV[1]; + if (substr($uC,0,2) eq "0x") {$uC = hex(substr($uC,2));} + if ($uC >= $num) { + die "This microcontroller Number is not allowed! \n"; + } + + # channel number + $ch = $ARGV[2]; + if (substr($ch,0,2) eq "0x") {$ch = hex(substr($ch,2));} + if ($ch >= 4) { + die "This channel does not exist\n"; + } + + $rw = "W"; + $reg = 0x1; + $val = $ARGV[3]; + if (substr($val,0,2) eq "0x") {$val= hex(substr($val,2));} + if ($val > 0xFFFF) { + die "The value is greater than 16 bit! Only 16 bit are allowed\n"; + } + my $cmd = sprintf("%s%02x0%01x%01x%04x",$rw,$uC,$ch,$reg,$val); + print $cmd."\n"; + PrintAnswer(Cmd($cmd)) #Answer without \n + } else { + die "Not all arguements were specified!\nUSAGE: dcdc.pl s \n"; + } +} + +# Read Setting of DCDC +if($mode == 3) {SendCmdShort(0x1)} + +# Temperature +if($mode == 4) {SendCmdShort(0x4)} + +# Voltage +if($mode == 5) {SendCmdShort(0x2)} + +# Current +if($mode == 6) {SendCmdShort(0x3)} + +# Infos +if($mode == 7) {SendCmdShort(0x5)} diff --git a/atmega32u4/dcdc_mdc/main.c b/atmega32u4/dcdc_mdc/main.c new file mode 100644 index 0000000..f6844aa --- /dev/null +++ b/atmega32u4/dcdc_mdc/main.c @@ -0,0 +1,674 @@ +// #define F_CPU 8000000UL + +#include +#include +#include +#include +#include +#include + +#define FIRMWARE_VERSION 0x001 + +/* +//ADC +// 0 Sense1 Vin +// 1 Sense_GND +// 4 Sense2 Vmon4 DCDC3 CH0 +// 5 Sense3 TEMP_OUT2 DCDC2 +// 6 Sense4 Vmon3 DCDC2 CH0 +// 7 Sense5 Vmon2 DCDC1 CH0 +// 10 Sense6 Vmon1 DCDC0 CH0 +// 11 Sense7 Cin + +// PB5 LED DCDC 0 (green) +// PB6 LED DCDC 1 (green) +// PC6 LED DCDC 2 (green) +// PC7 LED DCDC 3 (green) +// PD6 LED1 (yellow) +// PD4 LED2 (red) + +//PB7 Output enable + +//shift register outputs (times 4) Output 0 on MSB, Output 3 on LSB side +// CTRL10 (MSB) +// CTRL11 +// CTRL12 +// empty +// DCDC_EN //DCDC ON/OFF +// CTRL00 +// CTRL01 +// CTRL02 + +// | Registers | description | +// |-----------|--------------------------------------------------------------| +// | 0 | DCDC ON/OFF | +// | 1 | DCDC set voltage adjustment resistors | +// | 2 | Voltage V_in (RO) | +// | 3 | Current C_in (RO) | +// | 4 | Temperature (RO) | +// | 5 | [15:4] Firmware; [3:2] reserved; [1] Switch ; [0] LED (RO) | +// | 6 | Current Offset | +// | 7 | Voltage V_out (RO) | +// | 8 | Current C_out (NA) | +// | 9 | Sense GND (NA) | + +// **Data format:** *XuuGcRvvvv* +// **Data format:** *X000cRvvvv* +*/ + +#define ISMYADDR() (rxbuf[1] == '0' && rxbuf[2] == '0') + +#define LED1_ON() PORTD |= (1 << PD6) +#define LED1_OFF() PORTD &= ~(1 << PD6) + +#define LED2_ON() PORTD |= (1 << PD4) +#define LED2_OFF() PORTD &= ~(1 << PD4) + +#define LED_CH0_ON() PORTB |= (1 << PB5) +#define LED_CH0_OFF() PORTB &= ~(1 << PB5) +#define LED_CH1_ON() PORTB |= (1 << PB6) +#define LED_CH1_OFF() PORTB &= ~(1 << PB6) +#define LED_CH2_ON() PORTC |= (1 << PC6) +#define LED_CH2_OFF() PORTC &= ~(1 << PC6) +#define LED_CH3_ON() PORTC |= (1 << PC7) +#define LED_CH3_OFF() PORTC &= ~(1 << PC7) + +#define SHCP_HIGH() PORTB |= (1 << PB1) // SCLK +#define SHCP_LOW() PORTB &= ~(1 << PB1) + +#define STCP_HIGH() PORTB |= (1 << PB3) +#define STCP_LOW() PORTB &= ~(1 << PB3) + +#define SHIFT_DATA_LOW() PORTB |= (1 << PB2) // MOSI +#define SHIFT_DATA_HIGH() PORTB &= ~(1 << PB2) + +#define SHIFT_EN_OUTPUT() PORTB &= ~(1 << PB7) +#define SHIFT_DIS_OUTPUT() PORTB |= (1 << PB7) + +// declaration of needed variables + +uint16_t time; + +uint8_t rxcnt = 0, txpoint = 0; +uint8_t rxbuf[11]; +uint8_t txbuf[12]; +uint16_t adc[16]; // raw values from ADC +uint16_t V_in = 0; +uint16_t C_in = 0; +uint16_t temp = 0; +uint16_t information = 1; +int8_t curr_offset = 0; +uint32_t shift_register; +uint16_t read_DCDC_status; +uint16_t read_setting; +uint8_t isMaster = 0; +uint16_t V_out; +uint16_t C_out; +uint16_t GND; + +uint8_t nib_to_hex(uint16_t in, uint8_t nib) { + // convert integer or nibbles into hex value + uint8_t t = (in >> (nib * 4)) & 0xF; + if (t <= 9) { + return t + 0x30; + } + return t - 10 + 0x61; +} + +uint8_t hex_to_int(uint8_t h) {//assumes valid number + // convert hex value to integer + if (h < 0x40) return h - 0x30; + if (h < 0x50) return h - 0x41 + 10; + return h - 0x61 + 10; +} + +void send_answer_buf(uint8_t *b) { UCSR1B |= (1 << UDRIE1); } + // activate transmit interupt "ISR(USART1_UDRE_vect)" + +void send_answer_hex(uint8_t *rxbuf, uint16_t v) { + // modeling the answer message in hex value + txbuf[0] = 'A'; + txbuf[1] = rxbuf[1]; + txbuf[2] = rxbuf[2]; + txbuf[3] = rxbuf[3]; // Switch deactivated + txbuf[4] = rxbuf[4]; + txbuf[5] = rxbuf[5]; + txbuf[6] = nib_to_hex(v, 3); + txbuf[7] = nib_to_hex(v, 2); + txbuf[8] = nib_to_hex(v, 1); + txbuf[9] = nib_to_hex(v, 0); + txbuf[10] = '\n'; + txbuf[11] = 0; //string ends with zero + send_answer_buf(txbuf); +} + +void sub1(uint8_t *c1, uint8_t *c2) { + // need for chain boards to count down the board number + uint8_t b = hex_to_int(*c1) * 16 + hex_to_int(*c2); + b -= 1; + *c1 = nib_to_hex(b, 1); + *c2 = nib_to_hex(b, 0); +} + +void forward_msg(uint8_t i) { + // need for chain boards to send the command to next board + sub1(&rxbuf[1], &rxbuf[2]); + memcpy((uint8_t *)txbuf, (uint8_t *)rxbuf, i); + txbuf[i] = 0; + send_answer_buf(txbuf); +} + +uint8_t is_my_address(uint8_t s) { + // need for chain boards and handle to communicate to the right board + if (ISMYADDR()) { + // rxbuf[2] -= '0'; // write number to buffer, instead of ascii. + return 1; + } else { + forward_msg(s); + return 0; + } +} + +void setVoltages(void) { + // need to change the voltage by dis/enable resistor with the shiftregister "NPIC6C596A" + cli(); + STCP_LOW(); + uint32_t mask = (uint32_t)0x80000000; + for (int8_t i = 31; i >= 0; i--) { + SHCP_LOW(); + if (shift_register & mask) + SHIFT_DATA_HIGH(); + else + SHIFT_DATA_LOW(); + SHCP_HIGH(); + mask >>= 1; + } + SHCP_LOW(); + SHIFT_DATA_LOW(); + // Load to Output Register: + STCP_HIGH(); + STCP_HIGH(); + STCP_LOW(); + SHIFT_EN_OUTPUT(); + sei(); +} + +void switchDCDC(uint8_t group, uint8_t val) { + // de/activate the dcdc themself + if (val == 0) { + shift_register &= ~((uint32_t)1 << (4 + group * 8)); // DCDC off + if (group == 0) { + LED_CH0_OFF(); + } else if (group == 1) { + LED_CH1_OFF(); + } else if (group == 2) { + LED_CH2_OFF(); + } else if (group == 3) { + LED_CH3_OFF(); + } + } + else { + shift_register |= ((uint32_t)1 << (4 + group * 8)); // DCDC on + if (group == 0) { + LED_CH0_ON(); + } else if (group == 1) { + LED_CH1_ON(); + } else if (group == 2) { + LED_CH2_ON(); + } else if (group == 3) { + LED_CH3_ON(); + } + } + setVoltages(); +} + +void setDCDC(uint8_t group, uint8_t chan, uint8_t val) { + // enable resistor of the voltage devider to change the feedback line to control the dcdc voltage output + // DCDC CH0 + if (chan == 0 && val <= 7) { + val = 7 - val; + shift_register &= ~(((uint32_t)1 << ((5 + group * 8) + 2)) | ((uint32_t)1 << ((5 + group * 8) + 1)) | ((uint32_t)1 << (5 + group * 8))); // clear bitpositon f.e. group = 0 -> 7,6,5 + val = ((val >> 2) & 1) | (val & 2)| ((val <<2) & 4); //for lineal increase + shift_register |= (uint32_t)val << (group * 8 + 5); + } + // DCDC CH1 + if (chan == 1 && val <= 7) { + val = 7 - val; + shift_register &= ~(((uint32_t)1 << ((group * 8) + 2)) | ((uint32_t)1 << ((group * 8) + 1)) | ((uint32_t)1 << (group * 8))); // clear bitpositon f.e. group = 0 -> 2,1,0 + val = ((val >> 2) & 1) | (val & 2)| ((val <<2) & 4); + shift_register |= (uint32_t)val << (group * 8); + } + + setVoltages(); +} + +void setInfo(uint8_t chan, uint8_t val) { + if ((val & 0x1) == 1) {LED1_ON(); information |= (1 << 0);} // LED1 on + if ((val & 0x1) == 0) {LED1_OFF(); information &= ~(1 << 0);} // LED1 off + + if (((val >> 1) & 0x1) == 1) {LED2_ON(); information |= (1 << 1);} // LED2 on + if (((val >> 1) & 0x1) == 0) {LED2_OFF(); information &= ~(1 << 1);} // LED2 off + + information |= (FIRMWARE_VERSION << 4); +} + +/* +// Data: XuuGcRvvvv +// Switch deactivated +// X - command (write: W, read: R, answer: A etc.) +// uu - Controllernumber (Hex value) +// G - Groupnumber (to talk to all channels, that belong together, +in one command.) +// c - channelnumber in the group (Hex value) +// R - register (Hex value) +// vvvv - 16 Bit value +// +// All in all 10 characters +// RF2012FE51 +// +// close with a "\n" +// e.g. "RF2012FE51\n" +// +// +// +// | Registers | description | +// |-----------|--------------------------------------------------------------| +// | 0 | DCDC ON/OFF | +// | 1 | DCDC set voltage adjustment resistors | +// | 2 | Voltage V_in (RO) | +// | 3 | Current C_in (RO) | +// | 4 | Temperature (RO) | +// | 5 | [15:4] Firmware; [3:2] reserved; [1] Switch ; [0] LED (RO) | +// | 6 | Current Offset | +// | 7 | Voltage V_out (RO) | +// | 8 | Current C_out (NA) | +// | 9 | Sense GND (RO) | +*/ + +void getdata(uint8_t buf) { + // handle the incoming comand and call the correct function + if (rxcnt != 0 || (buf == 'A' || buf == 'W' || buf == 'R')) { + rxbuf[rxcnt++] = buf; + } + if (buf == '\n' || buf == '\r') { // End of Command + if (rxbuf[0] == 'A') { + // answer + memcpy((uint8_t *)txbuf, (uint8_t *)rxbuf, 10); + txbuf[11] = 0; + send_answer_buf(txbuf); + } else if (rxbuf[0] == 'S') { // Scann of chain, returns number of boards + txbuf[0] = 'S'; + uint8_t length = rxcnt - 2; // Length of Counter + // read current counter value + uint32_t cnt = 0; + if (length == 0) { + length = 1; + } else { + uint32_t base = 1; + for (uint8_t i = length; i > 0; i--) { + + cnt += hex_to_int(rxbuf[i]) * base; + base *= 16; + } + } + // inc counter value + cnt++; + // send to next uC + if ((cnt % 16) == 0) + length++; + for (uint8_t i = length; i > 0; i--) { + txbuf[i] = nib_to_hex(cnt, length - i); + } + if (length > 8) + length = 8; // skip to keep a clean ending of message + txbuf[length + 1] = 10; + txbuf[length + 2] = 0; + send_answer_buf(txbuf); + rxcnt = 0; + } else if (rxcnt == 11 && is_my_address(10)) { // message is for this uC +// } else if (is_my_address(10)) { //answer 0x00d1 works with this line + if (rxbuf[0] == 'W') { + // write + switch (hex_to_int(rxbuf[5])) { + + // set DCDC ON/OFF + case 0: + if (hex_to_int(rxbuf[3]) < 4) { + switchDCDC(hex_to_int(rxbuf[3]), hex_to_int(rxbuf[9])); + eeprom_update_byte((uint8_t *)0x21, shift_register); + eeprom_update_byte((uint8_t *)0x22, shift_register >> 8); + eeprom_update_byte((uint8_t *)0x23, shift_register >> 16); + eeprom_update_byte((uint8_t *)0x24, shift_register >> 24); + } + send_answer_hex(&rxbuf[0], 0x00d1); + break; + + // set voltage of DCDC-Converter + case 1: + if (hex_to_int(rxbuf[3]) < 4) { + setDCDC(hex_to_int(rxbuf[3]), hex_to_int(rxbuf[4]), hex_to_int(rxbuf[9])); + eeprom_update_byte((uint8_t *)0x21, shift_register); + eeprom_update_byte((uint8_t *)0x22, shift_register >> 8); + eeprom_update_byte((uint8_t *)0x23, shift_register >> 16); + eeprom_update_byte((uint8_t *)0x24, shift_register >> 24); + } + send_answer_hex(&rxbuf[0], 0x00d1); + break; + + // set information + case 5: + setInfo(hex_to_int(rxbuf[4]), hex_to_int(rxbuf[9])); + send_answer_hex(&rxbuf[0], information); + break; + + // set current offset + case 6: + curr_offset = (hex_to_int(rxbuf[7]) * 16 + hex_to_int(rxbuf[8])) & 0xFF; + eeprom_update_byte((uint8_t *)0x26, curr_offset); + send_answer_hex(&rxbuf[0], curr_offset * 16); + break; + + default: + send_answer_hex(&rxbuf[0], 0xFFFF); + break; + } + } + + if (rxbuf[0] == 'R') { + // read + + // get DCDC status + if (hex_to_int(rxbuf[5]) == 0) { + uint16_t read_DCDC_status = 0xFFFF; + // DCDC Status CH0 + if (hex_to_int(rxbuf[4]) == 0) { + read_DCDC_status = ((shift_register >> (4 + hex_to_int(rxbuf[3]) * 8)) & 1); + } + + send_answer_hex(&rxbuf[0], read_DCDC_status); + } + + // get voltage resistors selection + if (hex_to_int(rxbuf[5]) == 1) { + uint16_t read_setting = 0xFFFF; + // CH0 + if (hex_to_int(rxbuf[4]) == 0) { + read_setting = 7 - ((shift_register >> (hex_to_int(rxbuf[3]) * 8 + 5)) & 7); + } + // CH1 + if (hex_to_int(rxbuf[4]) == 1) { + read_setting = 7 - ((shift_register >> (hex_to_int(rxbuf[3]) * 8)) & 7); + } + send_answer_hex(&rxbuf[0], read_setting); + } + + // get voltage V_in + if (hex_to_int(rxbuf[5]) == 2) { + V_in = (5 * adc[0]) / 2; + send_answer_hex(&rxbuf[0], V_in); + } + + // get current C_in + if (hex_to_int(rxbuf[5]) == 3) { + C_in = (5 * adc[11]) / 2; + send_answer_hex(&rxbuf[0], C_in); + } + + // get temperature + if (hex_to_int(rxbuf[5]) == 4) { + temp = adc[5]; + send_answer_hex(&rxbuf[0], temp); + } + + // get information + if (hex_to_int(rxbuf[5]) == 5) { + information |= (FIRMWARE_VERSION << 4); + send_answer_hex(&rxbuf[0], information); + } + + // get current offset + if (hex_to_int(rxbuf[5]) == 6) { + send_answer_hex(&rxbuf[0], curr_offset * 16); + } + + // get voltage V_out + if (hex_to_int(rxbuf[5]) == 7) { + + // DCDC 0 + if (hex_to_int(rxbuf[3]) == 0) { + V_out = (5 * adc[10]) / 2; // 2.5 (5/2) stepsize of adc + send_answer_hex(&rxbuf[0], V_out); + }CSR1B |= (1 << UDRIE1); } + // DCDC 1 + if (hex_to_int(rxbuf[3]) == 1) { + V_out = (5 * adc[7]) / 2; + send_answer_hex(&rxbuf[0], V_out); + } + // DCDC 2 + if (hex_to_int(rxbuf[3]) == 2) { + V_out = (5 * adc[6]) / 2; + send_answer_hex(&rxbuf[0], V_out); + } + // DCDC 3 + if (hex_to_int(rxbuf[3]) == 2) { + V_out = (5 * adc[4]) / 2; + send_answer_hex(&rxbuf[0], V_out); + } + } + /* + // get current C_out + if (hex_to_int(rxbuf[5]) == 8) { + //LDO CH1 + if (hex_to_int(rxbuf[4]) == 1) { + C_out = adc[13]; + } + //LDO CH0 + if (hex_to_int(rxbuf[4]) == 0) { + C_out = adc[3]; + } + send_answer_hex(&rxbuf[0],C_out); + } + */ + + // get GND + if (hex_to_int(rxbuf[5]) == 9) { + GND = (5 * adc[1]) / 2; + ; + send_answer_hex(&rxbuf[0], GND); + } + } + } + if (rxcnt >= 11 || buf == '\n' || buf == '\r') { + rxcnt = 0; + } + } +} + + +ISR(ADC_vect) { + // interupt for reading adc and save the values + static uint8_t channel = 0; + adc[channel] = ADC; + + if (channel == 1) channel = 4; + else if (channel == 7) channel = 13; + else if (channel == 13) channel = 0; + else channel++; + + ADMUX &= 0xe0; + ADMUX |= (channel & 0xf); + if (channel == 13) + ADCSRB |= (1 << MUX5); + else + ADCSRB &= ~(1 << MUX5); + + ADCSRA |= (1 << ADSC); +} + +ISR(USART1_RX_vect) { + // interupt for incoming comand + uint8_t buf = UDR1; + if (isMaster == 0) { + getdata(buf); + } else { + // usb_serial_putchar(buf); + } +} + +ISR(USART1_UDRE_vect) { + // interupt for transmitting answer message + if (txbuf[txpoint] != 0) + UDR1 = txbuf[txpoint++]; + if (txpoint > 11 || txbuf[txpoint] == 0) { + txpoint = 0; + UCSR1B &= ~(1 << UDRIE1); // deactivate Transmit + } +} + +ISR(TIMER0_OVF_vect) { + // interupt for time, actual only need for watchdoc if programm crashed as reset function + time++; + asm volatile("wdr"); +} + +__attribute__((naked)) void main(void) { + + CLKPR = (1 << CLKPCE); // prescaler 2 = 8 MHz + CLKPR = (1 << CLKPS0); // prescaler 2 + + // Configure ports + + MCUCR |= (1 << JTD); + MCUCR |= (1 << JTD); // yes, twice + + //--------------------------------------------------------// + // DDRx : 0 = Input; 1 = Output + // PORTx : Input -> 0: no PullUp 1: Pullup + // Output -> 0: LOW 1: HIGH + + PORTB = 0b00000000; + DDRB = 0b11101110; + + PORTC = 0b00000000; + DDRC = 0b11000000; + + PORTD = 0b00001100; + DDRD = 0b01011000; + + PORTE = 0b00000000; + DDRE = 0b00000000; + + PORTF = 0b00000000; + DDRF = 0b00000000; + + // Timer0 at 30 Hz overflow for ADC control + TCCR0B = (5 << CS00); + TIMSK0 = (1 << TOIE0); // Overflow interrupt` + + // Init USART + UCSR1A = (1 << U2X1); // Double Speed Mode + // UCSR1A = (0 << U2X1); // Single Speed Mode + UCSR1B = (1 << RXCIE1) | (0 << TXCIE1) | (0 << RXEN1) | (1 << TXEN1); + UCSR1C = (3 << UCSZ10); // 8 Bit + UBRR1 = 0x10; // 38k4 (SSM) //0x33; // 38k4 //0x10; //57600 + _delay_ms(10); + UCSR1B |= (1 << RXEN1); + + // ADMUX = (3 << REFS0); //reference 2.56V internal + // ADCSRA = (1 << ADEN) | (0 << ADSC) | (1 << ADIE) | (7 << ADPS0); + // //enable, start, irq, /128 DIDR0 = 0b11111111; DIDR2 = (1 << ADC13D); + // ADMUX &= 0xe0; ADMUX |= 1; ADCSRB |= (1<= 0) { + // isMaster = 1; + // //SELECT_MASTER(); + // getdata(n); + // } + + if ((time != lasttime)) { + } + lasttime = time; + } +} diff --git a/atmega32u4/dcdc_mdc/usb_serial.c b/atmega32u4/dcdc_mdc/usb_serial.c new file mode 100644 index 0000000..708bc54 --- /dev/null +++ b/atmega32u4/dcdc_mdc/usb_serial.c @@ -0,0 +1,937 @@ +/* USB Serial Example for Teensy USB Development Board + * http://www.pjrc.com/teensy/usb_serial.html + * Copyright (c) 2008,2010,2011 PJRC.COM, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Version 1.0: Initial Release +// Version 1.1: support Teensy++ +// Version 1.2: fixed usb_serial_available +// Version 1.3: added transmit bandwidth test +// Version 1.4: added usb_serial_write +// Version 1.5: add support for Teensy 2.0 +// Version 1.6: fix zero length packet bug +// Version 1.7: fix usb_serial_set_control + +#define USB_SERIAL_PRIVATE_INCLUDE +#include "usb_serial.h" + + +/************************************************************************** + * + * Configurable Options + * + **************************************************************************/ + +// You can change these to give your code its own name. On Windows, +// these are only used before an INF file (driver install) is loaded. +#define STR_MANUFACTURER L"Xmatter" +#define STR_PRODUCT L"DCDC_Test" + +// All USB serial devices are supposed to have a serial number +// (according to Microsoft). On windows, a new COM port is created +// for every unique serial/vendor/product number combination. If +// you program 2 identical boards with 2 different serial numbers +// and they are assigned COM7 and COM8, each will always get the +// same COM port number because Windows remembers serial numbers. +// +// On Mac OS-X, a device file is created automatically which +// incorperates the serial number, eg, /dev/cu-usbmodem12341 +// +// Linux by default ignores the serial number, and creates device +// files named /dev/ttyACM0, /dev/ttyACM1... in the order connected. +// Udev rules (in /etc/udev/rules.d) can define persistent device +// names linked to this serial number, as well as permissions, owner +// and group settings. +//#define STR_SERIAL_NUMBER L ## NUMBER + + +// Mac OS-X and Linux automatically load the correct drivers. On +// Windows, even though the driver is supplied by Microsoft, an +// INF file is needed to load the driver. These numbers need to +// match the INF file. +#define VENDOR_ID 0x16C0 +#define PRODUCT_ID 0x047A + +// When you write data, it goes into a USB endpoint buffer, which +// is transmitted to the PC when it becomes full, or after a timeout +// with no more writes. Even if you write in exactly packet-size +// increments, this timeout is used to send a "zero length packet" +// that tells the PC no more data is expected and it should pass +// any buffered data to the application that may be waiting. If +// you want data sent immediately, call usb_serial_flush_output(). +#define TRANSMIT_FLUSH_TIMEOUT 5 /* in milliseconds */ + +// If the PC is connected but not "listening", this is the length +// of time before usb_serial_getchar() returns with an error. This +// is roughly equivilant to a real UART simply transmitting the +// bits on a wire where nobody is listening, except you get an error +// code which you can ignore for serial-like discard of data, or +// use to know your data wasn't sent. +#define TRANSMIT_TIMEOUT 25 /* in milliseconds */ + +// USB devices are supposed to implment a halt feature, which is +// rarely (if ever) used. If you comment this line out, the halt +// code will be removed, saving 116 bytes of space (gcc 4.3.0). +// This is not strictly USB compliant, but works with all major +// operating systems. +//#define SUPPORT_ENDPOINT_HALT + + + +/************************************************************************** + * + * Endpoint Buffer Configuration + * + **************************************************************************/ + +// These buffer sizes are best for most applications, but perhaps if you +// want more buffering on some endpoint at the expense of others, this +// is where you can make such changes. The AT90USB162 has only 176 bytes +// of DPRAM (USB buffers) and only endpoints 3 & 4 can double buffer. + +#define ENDPOINT0_SIZE 16 +#define CDC_ACM_ENDPOINT 2 +#define CDC_RX_ENDPOINT 3 +#define CDC_TX_ENDPOINT 4 +#if defined(__AVR_AT90USB162__) +#define CDC_ACM_SIZE 16 +#define CDC_ACM_BUFFER EP_SINGLE_BUFFER +#define CDC_RX_SIZE 32 +#define CDC_RX_BUFFER EP_DOUBLE_BUFFER +#define CDC_TX_SIZE 32 +#define CDC_TX_BUFFER EP_DOUBLE_BUFFER +#else +#define CDC_ACM_SIZE 16 +#define CDC_ACM_BUFFER EP_SINGLE_BUFFER +#define CDC_RX_SIZE 64 +#define CDC_RX_BUFFER EP_DOUBLE_BUFFER +#define CDC_TX_SIZE 64 +#define CDC_TX_BUFFER EP_DOUBLE_BUFFER +#endif + +static const uint8_t PROGMEM endpoint_config_table[] = { + 0, + 1, EP_TYPE_INTERRUPT_IN, EP_SIZE(CDC_ACM_SIZE) | CDC_ACM_BUFFER, + 1, EP_TYPE_BULK_OUT, EP_SIZE(CDC_RX_SIZE) | CDC_RX_BUFFER, + 1, EP_TYPE_BULK_IN, EP_SIZE(CDC_TX_SIZE) | CDC_TX_BUFFER +}; + + +/************************************************************************** + * + * Descriptor Data + * + **************************************************************************/ + +// Descriptors are the data that your computer reads when it auto-detects +// this USB device (called "enumeration" in USB lingo). The most commonly +// changed items are editable at the top of this file. Changing things +// in here should only be done by those who've read chapter 9 of the USB +// spec and relevant portions of any USB class specifications! + +static const uint8_t PROGMEM device_descriptor[] = { + 18, // bLength + 1, // bDescriptorType + 0x00, 0x02, // bcdUSB + 2, // bDeviceClass + 0, // bDeviceSubClass + 0, // bDeviceProtocol + ENDPOINT0_SIZE, // bMaxPacketSize0 + LSB(VENDOR_ID), MSB(VENDOR_ID), // idVendor + LSB(PRODUCT_ID), MSB(PRODUCT_ID), // idProduct + 0x00, 0x01, // bcdDevice + 1, // iManufacturer + 2, // iProduct + 3, // iSerialNumber + 1 // bNumConfigurations +}; + +#define CONFIG1_DESC_SIZE (9+9+5+5+4+5+7+9+7+7) +static const uint8_t PROGMEM config1_descriptor[CONFIG1_DESC_SIZE] = { + // configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10 + 9, // bLength; + 2, // bDescriptorType; + LSB(CONFIG1_DESC_SIZE), // wTotalLength + MSB(CONFIG1_DESC_SIZE), + 2, // bNumInterfaces + 1, // bConfigurationValue + 0, // iConfiguration + 0xC0, // bmAttributes + 50, // bMaxPower + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + 0, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x02, // bInterfaceClass + 0x02, // bInterfaceSubClass + 0x01, // bInterfaceProtocol + 0, // iInterface + // CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x00, // bDescriptorSubtype + 0x10, 0x01, // bcdCDC + // Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x01, // bDescriptorSubtype + 0x01, // bmCapabilities + 1, // bDataInterface + // Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28 + 4, // bFunctionLength + 0x24, // bDescriptorType + 0x02, // bDescriptorSubtype + 0x06, // bmCapabilities + // Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x06, // bDescriptorSubtype + 0, // bMasterInterface + 1, // bSlaveInterface0 + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC_ACM_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + CDC_ACM_SIZE, 0, // wMaxPacketSize + 64, // bInterval + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + 1, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x0A, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC_RX_ENDPOINT, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + CDC_RX_SIZE, 0, // wMaxPacketSize + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + CDC_TX_SIZE, 0, // wMaxPacketSize + 0 // bInterval +}; + +// If you're desperate for a little extra code memory, these strings +// can be completely removed if iManufacturer, iProduct, iSerialNumber +// in the device desciptor are changed to zeros. +struct usb_string_descriptor_struct { + uint8_t bLength; + uint8_t bDescriptorType; + int16_t wString[]; +}; +static const struct usb_string_descriptor_struct PROGMEM string0 = { + 4, + 3, + {0x0409} +}; +static const struct usb_string_descriptor_struct PROGMEM string1 = { + sizeof(STR_MANUFACTURER), + 3, + STR_MANUFACTURER +}; +static const struct usb_string_descriptor_struct PROGMEM string2 = { + sizeof(STR_PRODUCT), + 3, + STR_PRODUCT +}; +static const struct usb_string_descriptor_struct PROGMEM string3 = { + sizeof(STR_SERIAL_NUMBER), + 3, + STR_SERIAL_NUMBER +}; + +// This table defines which descriptor data is sent for each specific +// request from the host (in wValue and wIndex). +static const struct descriptor_list_struct { + uint16_t wValue; + uint16_t wIndex; + const uint8_t *addr; + uint8_t length; +} PROGMEM descriptor_list[] = { + {0x0100, 0x0000, device_descriptor, sizeof(device_descriptor)}, + {0x0200, 0x0000, config1_descriptor, sizeof(config1_descriptor)}, + {0x0300, 0x0000, (const uint8_t *)&string0, 4}, + {0x0301, 0x0409, (const uint8_t *)&string1, sizeof(STR_MANUFACTURER)}, + {0x0302, 0x0409, (const uint8_t *)&string2, sizeof(STR_PRODUCT)}, + {0x0303, 0x0409, (const uint8_t *)&string3, sizeof(STR_SERIAL_NUMBER)} +}; +#define NUM_DESC_LIST (sizeof(descriptor_list)/sizeof(struct descriptor_list_struct)) + + +/************************************************************************** + * + * Variables - these are the only non-stack RAM usage + * + **************************************************************************/ + +// zero when we are not configured, non-zero when enumerated +static volatile uint8_t usb_configuration=0; + +// the time remaining before we transmit any partially full +// packet, or send a zero length packet. +static volatile uint8_t transmit_flush_timer=0; +static uint8_t transmit_previous_timeout=0; + +// serial port settings (baud rate, control signals, etc) set +// by the PC. These are ignored, but kept in RAM. +static uint8_t cdc_line_coding[7]={0x00, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x08}; +static uint8_t cdc_line_rtsdtr=0; + + +/************************************************************************** + * + * Public Functions - these are the API intended for the user + * + **************************************************************************/ + +// initialize USB serial +void usb_init(void) +{ + HW_CONFIG(); + USB_FREEZE(); // enable USB + PLL_CONFIG(); // config PLL, 16 MHz xtal + while (!(PLLCSR & (1< size) write_size = size; + size -= write_size; + + // write the packet + switch (write_size) { + #if (CDC_TX_SIZE == 64) + case 64: UEDATX = *buffer++; + case 63: UEDATX = *buffer++; + case 62: UEDATX = *buffer++; + case 61: UEDATX = *buffer++; + case 60: UEDATX = *buffer++; + case 59: UEDATX = *buffer++; + case 58: UEDATX = *buffer++; + case 57: UEDATX = *buffer++; + case 56: UEDATX = *buffer++; + case 55: UEDATX = *buffer++; + case 54: UEDATX = *buffer++; + case 53: UEDATX = *buffer++; + case 52: UEDATX = *buffer++; + case 51: UEDATX = *buffer++; + case 50: UEDATX = *buffer++; + case 49: UEDATX = *buffer++; + case 48: UEDATX = *buffer++; + case 47: UEDATX = *buffer++; + case 46: UEDATX = *buffer++; + case 45: UEDATX = *buffer++; + case 44: UEDATX = *buffer++; + case 43: UEDATX = *buffer++; + case 42: UEDATX = *buffer++; + case 41: UEDATX = *buffer++; + case 40: UEDATX = *buffer++; + case 39: UEDATX = *buffer++; + case 38: UEDATX = *buffer++; + case 37: UEDATX = *buffer++; + case 36: UEDATX = *buffer++; + case 35: UEDATX = *buffer++; + case 34: UEDATX = *buffer++; + case 33: UEDATX = *buffer++; + #endif + #if (CDC_TX_SIZE >= 32) + case 32: UEDATX = *buffer++; + case 31: UEDATX = *buffer++; + case 30: UEDATX = *buffer++; + case 29: UEDATX = *buffer++; + case 28: UEDATX = *buffer++; + case 27: UEDATX = *buffer++; + case 26: UEDATX = *buffer++; + case 25: UEDATX = *buffer++; + case 24: UEDATX = *buffer++; + case 23: UEDATX = *buffer++; + case 22: UEDATX = *buffer++; + case 21: UEDATX = *buffer++; + case 20: UEDATX = *buffer++; + case 19: UEDATX = *buffer++; + case 18: UEDATX = *buffer++; + case 17: UEDATX = *buffer++; + #endif + #if (CDC_TX_SIZE >= 16) + case 16: UEDATX = *buffer++; + case 15: UEDATX = *buffer++; + case 14: UEDATX = *buffer++; + case 13: UEDATX = *buffer++; + case 12: UEDATX = *buffer++; + case 11: UEDATX = *buffer++; + case 10: UEDATX = *buffer++; + case 9: UEDATX = *buffer++; + #endif + case 8: UEDATX = *buffer++; + case 7: UEDATX = *buffer++; + case 6: UEDATX = *buffer++; + case 5: UEDATX = *buffer++; + case 4: UEDATX = *buffer++; + case 3: UEDATX = *buffer++; + case 2: UEDATX = *buffer++; + default: + case 1: UEDATX = *buffer++; + case 0: break; + } + // if this completed a packet, transmit it now! + if (!(UEINTX & (1<= NUM_DESC_LIST) { + UECONX = (1< desc_length) len = desc_length; + do { + // wait for host ready for IN packet + do { + i = UEINTX; + } while (!(i & ((1<= 1 && i <= MAX_ENDPOINT) { + usb_send_in(); + UENUM = i; + if (bRequest == SET_FEATURE) { + UECONX = (1< + +// setup +void usb_init(void); // initialize everything +uint8_t usb_configured(void); // is the USB port configured + +// receiving data +int16_t usb_serial_getchar(void); // receive a character (-1 if timeout/error) +uint8_t usb_serial_available(void); // number of bytes in receive buffer +void usb_serial_flush_input(void); // discard any buffered input + +// transmitting data +int8_t usb_serial_putchar(uint8_t c); // transmit a character +int8_t usb_serial_putchar_nowait(uint8_t c); // transmit a character, do not wait +int8_t usb_serial_write(const uint8_t *buffer, uint16_t size); // transmit a buffer +void usb_serial_flush_output(void); // immediately transmit any buffered output + +// serial parameters +uint32_t usb_serial_get_baud(void); // get the baud rate +uint8_t usb_serial_get_stopbits(void); // get the number of stop bits +uint8_t usb_serial_get_paritytype(void);// get the parity type +uint8_t usb_serial_get_numbits(void); // get the number of data bits +uint8_t usb_serial_get_control(void); // get the RTS and DTR signal state +int8_t usb_serial_set_control(uint8_t signals); // set DSR, DCD, RI, etc + +// constants corresponding to the various serial parameters +#define USB_SERIAL_DTR 0x01 +#define USB_SERIAL_RTS 0x02 +#define USB_SERIAL_1_STOP 0 +#define USB_SERIAL_1_5_STOP 1 +#define USB_SERIAL_2_STOP 2 +#define USB_SERIAL_PARITY_NONE 0 +#define USB_SERIAL_PARITY_ODD 1 +#define USB_SERIAL_PARITY_EVEN 2 +#define USB_SERIAL_PARITY_MARK 3 +#define USB_SERIAL_PARITY_SPACE 4 +#define USB_SERIAL_DCD 0x01 +#define USB_SERIAL_DSR 0x02 +#define USB_SERIAL_BREAK 0x04 +#define USB_SERIAL_RI 0x08 +#define USB_SERIAL_FRAME_ERR 0x10 +#define USB_SERIAL_PARITY_ERR 0x20 +#define USB_SERIAL_OVERRUN_ERR 0x40 + +// This file does not include the HID debug functions, so these empty +// macros replace them with nothing, so users can compile code that +// has calls to these functions. +#define usb_debug_putchar(c) +#define usb_debug_flush_output() + + + +// Everything below this point is only intended for usb_serial.c +#ifdef USB_SERIAL_PRIVATE_INCLUDE +#include +#include +#include + +#define EP_TYPE_CONTROL 0x00 +#define EP_TYPE_BULK_IN 0x81 +#define EP_TYPE_BULK_OUT 0x80 +#define EP_TYPE_INTERRUPT_IN 0xC1 +#define EP_TYPE_INTERRUPT_OUT 0xC0 +#define EP_TYPE_ISOCHRONOUS_IN 0x41 +#define EP_TYPE_ISOCHRONOUS_OUT 0x40 +#define EP_SINGLE_BUFFER 0x02 +#define EP_DOUBLE_BUFFER 0x06 +#define EP_SIZE(s) ((s) == 64 ? 0x30 : \ + ((s) == 32 ? 0x20 : \ + ((s) == 16 ? 0x10 : \ + 0x00))) + +#define MAX_ENDPOINT 4 + +#define LSB(n) (n & 255) +#define MSB(n) ((n >> 8) & 255) + +#if defined(__AVR_AT90USB162__) +#define HW_CONFIG() +#define PLL_CONFIG() (PLLCSR = ((1<