/* Firmware für den ATmega32U4
Zuständig: Felix Pfeiffer
Henrik Haftmann
230605 erstellt
+211028 Anpassung an silberne Ofensteuerung
Aufgaben:
1. USB-Interface für WebUSB, hier mal ohne Bulk-Pipes, nur EP0
2. I²C-Master 400 kHz
Hardware:
Pro Micro, idealerweise als 3,3-V-Version mit 16-MHz-Quarz (gibt's nicht!)
╔═════╦═════╦═════╗
─╢TxD ║ USB ║ RAW╟─
RPi (Ersatz) ─╢RxD ║ ║ GND╟─ ● = LED
─╢GND ╚═════╝ RST╟─
3P3 ───┐ ┌─── GND ─╢GND ● Ucc╟─
SDA ───┼─┼─── D1/SDA ─╢2 Pwr A3╟─
SCL ───┼─┼─── D0/SCL ─╢3 A2╟─
P4 └─┼─── D4/3P3 ─╢4 A1╟─
GND ─────┘ ─╢5 A0╟─
─╢6 15╟─
─╢7 Rx Tx 14╟─
─╢8 ● ● 16╟─
─╢9 10╟─
╚═════════════════╝
µC Pin Port Funkt. Signal Anwendung
8 PB0 SS -- rote LED „RX“, lo-aktiv
9 PB1 SCL -
10 PB2 MOSI -
11 PB3 MISO -
28 PB4 -
29 PB5 OC1A -
30 PB6 OC1B -
12 PB7 OC1C --
31 PC6 OC3A -
32 PC7 (ICP3) --
18 PD0 SCL SCL I²C-Anschluss (externer Pullup erforderlich!)
19 PD1 SDA SDA I²C-Anschluss (externer Pullup erforderlich!)
20 PD2 RxD -
21 PD3 TxD -
25 PD4 ICP1 3P3 High-Ausgang zur Vereinfachung des Steckers
22 PD5 (XCK) -- rote LED „TX“, lo-aktiv
26 PD6 ADC9 --
27 PD7 ADC10 -
33 PE2 !HWB --
1 PE6 AIN0+ -
41 PF0 ADC0 --
40 PF1 ADC1 --
39 PF4 ADC4 -
38 PF5 ADC5 -
37 PF6 ADC6 -
36 PF7 ADC7 -
3 - D- USB Data-
4 - D+ USB Data+
7 - Ubus USB-Busspannung
13 - !Reset Reset-Eingang: Taste
16 - XTAL2 16-MHz-Quarz
17 - XTAL1 16-MHz-Quarz
42 - AREF Referenzspannung 2,0 V
6 - UCAP Kondensator
2,34,14,44,24 UUcc,Ucc,AUcc 5P
5,15,23,35,43 UGND,GND GND
*/
#include <avr/io.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h> // PSTR
//#include <avr/interrupt.h>
#include <avr/signature.h>
#include <util/delay.h>
#include "usb.h"
#include <string.h>
/**********************
* Urlader anspringen *
**********************/
static word bootmagic NOINIT;
// Kode wird vor RAM-Initialisierung eingefügt
extern "C" void init3() __attribute__((used,naked,section(".init3")));
void init3() {
if (bootmagic==4711) {
bootmagic=0; // nur einmal
asm("jmp 0x7E00"); // Adresse des Urladers (hier: ubaboot) anspringen
}
MCUCR = 0x80; // JTAG deaktivieren (sonst gehen PF4..PF7 = ADC4..ADC7 nicht)
CLKPR = 0x80;
CLKPR = 0; // Volle Taktfrequenz 16 MHz
MCUSR = 0;
WDTCSR= 0x18; // Watchdog töten
WDTCSR= 0;
}
[[noreturn]] void bootstart() __attribute__((naked,noreturn));
void bootstart() {
USBCON=0x20; // USB terminieren: Pullup-Widerstand entfernen, das lässt sofort die Verbindung kappen
UDCON =0x01;
// cli();
MCUSR =0x02; // Reset vom Eingang vorgaukeln
bootmagic=4711;
WDTCSR=0x18;
WDTCSR=0x0F; // 2 s aktivieren
for(;;); // Sauberen Watchdog-Reset auslösen
}
// Ich nahm an, dass der Controller stets bei Reset nach 0x7E00 springt
// und der Urlader MCUSR und PE2/!HWB einliest, aber das ist *nicht* der Fall!
// Die *Hardware* liest PE2/!HWB ein und springt bei !Reset && !!HWB
// nach 0x7E00, sonst nach 0x0000. IMHO irrsinnig, PICs lösen das in Software.
// Um dem Urlader ein lupenreines Reset anzubieten, geht es deshalb
// über einen Watchdog-Reset und über ein magisches Speicherwort
// beim nächsten Hochfahren zum Urlader.
/*************************************
* Initialisierung und Hauptprogramm *
*************************************/
static void setupHardware() {
MCUCR = 0x80; // JTAG deaktivieren (sonst gehen PF4..PF7 = ADC4..ADC7 nicht)
CLKPR = 0x80;
CLKPR = 0; // Takt auf volle 16 MHz
PORTB = 0xFF; // Alle Pullups
DDRB = 0x01; // Rx-LED aus
PORTC = 0xC0; // Alle Pullups
PORTD = 0xFF; // Alle Pullups (werden an I²C D0,D1 unwirksam)
DDRD = 0x30; // Tx-LED aus, 3P3-Ausgang (für externen Pullup) ein
PORTE = 0x44; // Alle Pullups
PORTF = 0xF3; // Alle Pullups
ACSR = 0x80; // Kein Analogvergleicher
TWBR = F_CPU/400000-16>>1; // = 12
TWCR = 0x04; // I²C aktivieren
}
static void idle() {
usb::Poll();
}
// Hier: Nur für <=64 Byte
static byte usbEp0Read() {
while (UENUM=0, !(UEINTX&1<<2)) usb::Poll();
return UEDATX;
}
static void usbEp0Write(byte b) {
UENUM=0;
UEDATX = b;
}
static void usbEp0Commit() {
UENUM=0;
UEINTX=0b1111'1110;
}
static bool usbOkay() {return true;}
namespace i2c{
static byte waitint(byte=0x84); // Default: Kein Start, kein Stop, NAK
static void waitstop();
static bool isTenBit(word);
static byte send(word,word,word);
static byte recv(word,word,word);
}
byte i2c::lastError;
static byte i2c::waitint(byte twcr) {
TWCR = twcr;
while (!(TWCR&0x80)) usb::Poll();
return TWSR;
}
static void i2c::waitstop() {
TWCR=0x94; // Interrupt löschen, STOP auslösen
while (TWCR&0x10) usb::Poll(); // warten bis STOP zu Ende
}
static bool i2c::isTenBit(word wValue) {
return (wValue&0x3FF)>=0x80 || wValue>>8&f10bit;
}
static byte i2c::send(word wValue, word wIndex, word wLength) {
byte f = wValue>>8;
byte e;
if (!(f&fNoStart)) { // Startsequenz und Adresse unterdrücken?
byte r = waitint(0xA4); // START senden
e=eNoStart;
if (r!=0x08 && r!=0x10) goto stop;
e=eNoAddr;
if (isTenBit(wValue)) { // 10-Bit-Adresse (W)
TWDR = 0xF0|(wValue>>8&3)<<1;// 1111 0aa0
if (waitint()!=0x18) goto stop;
TWDR = wValue;
if (waitint()!=0x28) goto stop;
}else{ // 7-Bit-Adresse (W)
TWDR = wValue<<1;
if (waitint()!=0x18) goto stop; // ACK ist Pflicht
}
}
if (f&fAddr) { // Adressbytes aus wIndex senden?
e=eNoSub;
TWDR = f&fAddr8 ? wIndex : wIndex>>8;
if (waitint()!=0x28) goto stop;
if (f&fAddrHL) {
TWDR = f&fAddr8 ? wIndex>>8 : wIndex;
if (waitint()!=0x28) goto stop;
}
}
if (wLength) for(;;) {
e=eUsbHickup;
TWDR = usbEp0Read();
if (!usbOkay()) goto stop;
e=eEarlyNak;
byte r = waitint();
if (--wLength) {
if (r==0x28) continue; // ACK ist Pflicht
}else{
if (r==0x28 || !(f&fNoStop) && r==0x30) break;
}
goto stop; // ACK ist Pflicht bei NoStop
}
e=0;
if (!(f&fNoStop)) stop: waitstop();
return e;
}
bool i2c::send() {
PORTD&=~(1<<5);// Tx-LED ein
bool ret=!(lastError=send(usb::setup.wValue,usb::setup.wIndex,usb::setup.wLength));
PORTD|=1<<5; // Tx-LED aus
return ret;
}
static byte i2c::recv(word wValue,word wIndex,word wLength) {
byte f = wValue>>8;
byte e;
if (!(f&fNoStart)) {
if (f&fAddr) { // Adressangabe?
if (f&fNoRep) wValue&=~(fNoStop<<8);
else wValue|=fNoStop<<8; // 1-2 Bytes schreiben = Adresse setzen, typischerweise ohne STOP
if (e=send(wValue,wIndex,0)) return e;
}
e=eNoStart+eRead;
byte r = waitint(0xA4); // START senden
if (r!=0x08 && r!=0x10) goto stop;
e=eNoAddr+eRead;
TWDR = isTenBit(wValue) // 10-Bit-Adresse (R)
? 0xF0|(wValue>>8&3)<<1|1 // 1111 0aa1
: wValue<<1|1; // 7-Bit-Adresse (R)
if (waitint()!=0x40) goto stop; // ACK ist Pflicht
}
if (wLength) do{
e=eEarlyNak+eRead;
byte r = waitint(--wLength || f&fNoStop ? 0xC4 : 0x84); // NAK beim letzten Byte
if (r!=0x50 && r!=0x58) goto stop;
e=eUsbHickup+eRead;
usbEp0Write(TWDR);
if (!usbOkay()) goto stop;
}while (wLength);
usbEp0Commit();
e=0;
if (!(f&fNoStop)) stop: waitstop();
return e;
}
bool i2c::recv() {
PORTB&=~(1<<0);// Rx-LED ein
bool ret=!(lastError=recv(usb::setup.wValue,usb::setup.wIndex,usb::setup.wLength));
PORTB|=1<<0; // Rx-LED aus
return ret;
}
int main() {
setupHardware();
usb::Init();
// sei();
for(;;) idle();
}
Detected encoding: UTF-8 | 0
|