Source file: /~heha/mb-iwp/Antriebe/Linak-Servo/hea-fw-230331.zip/hea.cpp

#include "hea.h"

void basic_delay(uint32_t timeout) {
 do __NOP(); while (--timeout);
}

static uint32_t bootmagic;
const uint32_t BOOTMAGIC=0xDEADBEEF;

static void bootstart() __attribute__((naked,noreturn));
static void bootstart() {
 bootmagic=BOOTMAGIC;
 SCB->AIRCR = 0x05FA0004;
}
// Im Gegensatz zum PIC16F145x oder ATmegaX8/XUY
// ist der Bootloader nicht vor versehentlichem
// Überschreiben (per Fuse) geschützt. 220120 war er mal weg.

RepF repF={2,{0,1,0,1},1024,900,50,100,elemof(RepT::data),920,0x1698};
RepI repI;
RepT repT;
RepL repL;
byte RepT::sendcount;	// Nur für WebUSB = Bulk-Endpoint 2

void RepI::reset() {
 __disable_irq();
 flags&=0x0F;	// Low-Nibble bleibt bestehen
 __enable_irq();
}

template<class T, class U>void limit(T&v,U min,U max) {
 if (v<min) v=min;
 if (v>max) v=max;
}
template<class T>T abs(T v) {return v<0 ? -v : v;}

bool usb::onSetReport(usb::SetupPacket&setup) {
 union{
  RepF repF;
  RepL repL;
  byte repid;
  operator byte*() {return &repid;}
 }recvbuf;
 uint16_t len=0;
 switch (setup.wValue) {
  case 0x0302: len=sizeof(RepF); break;
  case 0x0304: len=sizeof(RepL); break;
  case 0x022A:
  case 0x0363: len=sizeof(RepReboot); break;
 }
 if (!len) return false;		// falsches setup.wValue
 if (setup.wLength!=len) return false;	// falsche Transaktionslänge
 bool ret=usb::recv(0,recvbuf,len);
 if (recvbuf.repid!=setup.wValueL) return false;	// falsche Report-ID
 switch (setup.wValue) {
  case 0x022A: if (recvbuf[1]==1) adc::manualtrigger(); break;
  case 0x0363: if (recvbuf[1]==1) bootstart(); break;
  case 0x0302: {
   byte change=0;
   if (repF.f.bk!=recvbuf.repF.f.bk) change|=0x10;
   if ((repF.f^recvbuf.repF.f)&0xFC) change|=adc::loadF;
   repF.f=recvbuf.repF.f;
// Einzelwertweise übertragen
   if ((unsigned)recvbuf.repF.brems<4096
   && repF.brems!=recvbuf.repF.brems) {
    repF.brems=recvbuf.repF.brems;
    change|=0x20;
   }
   if ((unsigned)recvbuf.repF.tv<4096	// muss >=0 sein!
   && repF.tv!=recvbuf.repF.tv) {repF.tv=recvbuf.repF.tv; change|=adc::loadT;}
   if (abs<int>(recvbuf.repF.th)<1024
   && repF.th!=recvbuf.repF.th) {repF.th=recvbuf.repF.th; change|=adc::loadT;}
   if ((unsigned)recvbuf.repF.tg<=elemof(repT.data)
   && repF.tg!=recvbuf.repF.tg) {repF.tg=recvbuf.repF.tg/12*12; change|=adc::loadTg;}
   if (recvbuf.repF.tp<=repF.tg	// hier: beliebig negativ erlaubt
   && repF.tp!=recvbuf.repF.tp) {repF.tp=recvbuf.repF.tp; change|=adc::loadTp;}
   if ((unsigned)recvbuf.repF.knull<4096
   && repF.knull!=recvbuf.repF.knull) {
				repF.knull=recvbuf.repF.knull; change|=0x40;}
   if (change&0x30 && repF.f.bk) pwm::out(repF.brems);
   adc::loadFeature(change&0x0F);
  }break;
 }
 return ret;
}

bool usb::onGetReport(usb::SetupPacket&setup) {
 switch (setup.wValue) {
  case 0x0101: return usb::send0(repI,sizeof repI);
  case 0x0302: return usb::send0(repF,sizeof repF);
  case 0x0303: return usb::send0(repT,sizeof repT);
  case 0x0304: return usb::send0(repL,sizeof repL);
 }
 return false;
}

static void USB_Renum() {
 BB_BIT(GPIOA->CRH,16) = 1;	// PA12: Open Drain Output
 BB_BIT(GPIOA->ODR,12) = 0;	// PA12 nach Low ziehen
 basic_delay(0x20000);
 BB_BIT(GPIOA->ODR,12) = 1;	// PA12 nach High freilassen
 BB_BIT(GPIOA->CRH,16) = 0;	// PA12: Eingang
}

void set_sysclock() {		// auf 72 MHz
 uint32_t cr=RCC->CR;
 RCC->CR=cr|=1<<16;		// 8-MHz-Quarz aktivieren
 while (!(RCC->CR&1<<17));	// warten
 uint32_t cfgr = 0<<22		// USB-Takt  = 72 MHz ÷ 1,5 = 48 MHz (exakt)
	| RCC_CFGR_PLLMULL9	// PLL-Takt  =  8 MHz × 9 = 72 MHz
	| RCC_CFGR_PLLSRC_HSE 	// PLL als Systemtakt
	| RCC_CFGR_ADCPRE_DIV6	// ADC-Takt  = 72 MHz ÷ 6 = 12 MHz (14 MHz max.)
	| RCC_CFGR_PPRE2_DIV1	// ABP2-Takt = 72 MHz ÷ 1 = 72 MHz (max.)
	| RCC_CFGR_PPRE1_DIV2	// ABP1-Takt = 72 MHz ÷ 2 = 36 MHz (max.)
	| RCC_CFGR_HPRE_DIV1;	// AHB-Takt  = 72 MHz ÷ 1 = 72 MHz (max.)
 RCC->CFGR=cfgr;
 RCC->CR=cr|=1<<24;		// PLL aktivieren
 while (!(RCC->CR&1<<25));	// warten
 FLASH->ACR=0x32;		// Flash benötigt für 72 MHz 2 Waitstates
 RCC->CFGR=cfgr|=2;		// Jetzt PLL als Systemtakt
 while (!(RCC->CFGR&1<<3));	// Warten
}

void revert_sysclock() {
 BB_BIT(RCC->CFGR,1) = 0;
 BB_BIT(RCC->CR,24) = 0;
 RCC->CFGR = 0;
 BB_BIT(FLASH->ACR,0) = 0;
 BB_BIT(RCC->CR,16) = 0;
}

// Aufruf in Hauptschleife aber auch aus usb::poll und usb::send heraus,
// daher unbedingt Rekursionen verhindern
void idle() {
 static byte lock;	// alles erlaubt
 if (!BB_BIT(lock,0)) {	// USB EP0
  BB_BIT(lock,0)=1;
  usb::poll();
  BB_BIT(lock,0)=0;
 }
 if (!BB_BIT(lock,1)) {	// USB sonstige Endpoints
  BB_BIT(lock,1)=1;
  if (repI.flags&0xF0) {	// Zuerst der Summary-Report
// Während HID-Reports vom usbhid.sys ausgelesen werden, auch wenn keine Äpp
// sich dafür interessiert …
   usb::send(1|usb::onBusyWait,repI,sizeof repI);
// … sammeln sich Bulk-Daten bei WebUSB an, wenn niemand diese abholt.
// Daher alte Daten rotzfrech überschreiben
   usb::send(2|usb::onBusySend,repI,sizeof repI);
   repI.reset();
  }else if (repT.sendcount) {	// danach(!) der Trace-Report
   debug::isrstart();
// Ein Timeout von 2 ms sorgt zwischen den Paketen für Abbruch,
// wenn sich bspw. die Web-Äpp mittendrin verabschiedet.
   usb::send(2|usb::onBusySend,repT,repT.byteLength());
   repT.sendcount--;	// Auch bei Sende-Fehler dekrementieren!
   debug::isrend();
  }
  BB_BIT(lock,1)=0;
 }
 static byte tic;	// für flotte 10 Hz Datenaktualisierung
 byte toc = USB.FNR;
 if (byte(toc-tic)>=100) {
  tic+=100;
// Da repI.flags auch in ISRs beeinflusst wird,
// über atomare Bitsetzfunktion gehen
  BB_BIT(repI.flags,7) = 1;	// nach 100 ms Update erzwingen
  adc::get_allstat();
// AC-Trigger mit 10 Hz nachführen
   if (repF.f.tk && repF.settrigger(repI.os[repF.f.tq].avg)&0x10) {
    adc::loadFeature(adc::loadT);
    BB_BIT(repI.flags,6) = 1;	// Veränderung in repF melden
   }
// Potenziometerwert mit 10 Hz einlesen
  int q = repI.p.avg;	// alter Wert zum Vergleich
  auto v=adc::get_poti(repI.p);
  q-=v; if (q<0) q=-q;
  if (repF.f.bk && q>=16) {	// Wenn am Poti gefummelt wurde
   repF.f.bk = 0;		// Fernsteuerung stehlen
   BB_BIT(repI.flags,6) = 1;	// Veränderung in repF melden
  }
// D/A-Wandler mit 10 Hz aktualisieren
  if (!repF.f.bk) pwm::out(v);
// Verdrehtaster mit 10 Hz einlesen
  BB_BIT(repI.flags,0) = ~GPIOA->IDR>>6&1;
// Bit 0 reflektiert Zustand des Verdreh-Tasters
 }
}

static void meminit() {
// Durch 4 teilbare Adressen werden durch das Linkerskript sichergestellt.
 extern uint32_t __ctors,__etext,__data,__bss,__noinit;
 auto d=&__data;
_Pragma("GCC diagnostic push")
_Pragma("GCC diagnostic ignored \"-Warray-bounds\"")
 auto s=&__etext+2;
// Aus unerfindlichem Grund ist da eine 8-Byte-Lücke im hea.bin!
// Genauso wenn man hea.hex generiert.
// Meine Vermutung: Rechenfehler in objcopy mit dem Vektor-Vorspann.
 while (d!=&__bss)    *d++=*s++;	// initialize data
_Pragma("GCC diagnostic pop") 
 while (d!=&__noinit) *d++=0;		// clear BSS
 auto fp=&__ctors;			// list of static constructors
 while (fp!=&__etext) reinterpret_cast<func_ptr_t>(*fp++)();	// call them
}

func_ptr_t vtbl[16+56] __attribute__((section(".vtbl")));
static_assert(sizeof vtbl==0x120);	// initialisierte Daten ab 0x20000520

static void Reset_Handler(uint32_t) __attribute__((naked,noreturn));
static void Reset_Handler(uint32_t) {
 if (bootmagic==BOOTMAGIC) {
  bootmagic=0;
  asm volatile(
"	movs	r1,#0		\n"	// movs ist kürzer als mov!
"	ldr	r0,[r1]		\n"	// Stackpointer von Flash-Adresse 0
"	msr	msp,r0		\n"	// einsetzen
"	ldr	r0,[r1,#4]	\n"	// Ansprungadresse von Flash-Adresse 4
"	bx	r0		\n");	// anspringen
 }
 set_sysclock();
 meminit();
 SCB->VTOR=uint32_t(vtbl);	// Vektortabelle in RAM umsetzen
 RCC->AHBENR |= 1;	// DMA aktivieren (Register bei Reset nicht Null!)
 RCC->APB1ENR = 0x00840007;	// USB, USART3 und Timer4:2 takten
 RCC->APB2ENR = 0x0000160D;	// SPI1, ADC2:1, PortB:A, Alternate Port Function (für ADC)
// Die Ports C und D werden nur gebraucht,
// wenn man am BluePill40/BlackPill34-Board
// an den Quarzen herumlötet und die entsprechenden Pins frei macht.
// So genügen Port A und B.
 GPIOA->BSRR = 0x00E0;		// Pullups an A7:5 aktiveren
 GPIOA->CRL = 0x88809888;	// A7:5 auf High ziehen, A3 = PWM-Ausgang
 GPIOA->CRH = 0x88854118;	// alles ungenutzte auf auf Low ziehen
 GPIOB->CRL = 0x88888800;	// B1:0 = Analogeingänge
 GPIOB->CRH = 0x88864A88;	// B12:10 = LED, RxD, TxD aktivieren
// 0 = Analoger Eingang				(ADC)
// 1 = Ausgang 10 MHz
// 2 = Ausgang 2 MHz
// 3 = Ausgang 50 MHz
// 4 = Eingang ohne Pullup/Pulldown		(USB,RxD)
// 5 = Open-Drain-Ausgang 10 MHz		(USB kurzzeitig)
// 6 = Open-Drain-Ausgang 2 MHz			(LED)
// 7 = Open-Drain-Ausgang 50 MHz
// 8 = Eingang mit Pullup/Pulldown je nach ODR	(Taster, LM393; freie Pins)
// 9 = Alternativer Totempfahl-Ausgang 10 MHz	(PWM)
// A = Alternativer Totempfahl-Ausgang 2 MHz	(TxD)
// B = Alternativer Totempfahl-Ausgang 50 MHz
// C = undefiniert
// D = Alternativer Open-Drain-Ausgang 10 MHz
// E = Alternativer Open-Drain-Ausgang 2 MHz
// F = Alternativer Open-Drain-Ausgang 50 MHz
 repI.repid=1;		// Report-Header initialisieren
 repT.repid=3;	// (nicht in .data für nur 1 Byte pro Struktur)
 repL.repid=4;
 adc::init();
 pwm::init();
 caliper::init();
// LED_INIT();
 USB_Renum();
 usb::init();
 for(;;) idle();
}

/* Minimal Flash-based vector table */
extern int32_t __stacktop;
func_ptr_t vectors[] __attribute__((section(".vectors"))) = {
 (func_ptr_t)&__stacktop,	// Initial stack pointer (MSP)
 (func_ptr_t)Reset_Handler,	// Initial program counter (PC): Reset handler
};
Detected encoding: UTF-80