Quelltext /~heha/mb-iwp/Antriebe/Linak-Servo/hips-210406.zip/hips.cpp

#include "hips.h"

void 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 ist der Bootloader nicht vor versehentlichem
// Überschreiben (per Fuse) geschützt. 220120 war er mal weg.

RepF repF={1,0,1024,1200,1400,50,elemof(RepStoß::kraft)};
RepI repI;
RepStoß repStoß;
RepLinak repLinak;

void RepI::reset() {
 n=0;
 p.reset();
 k.reset();
 flags&=~0xF2;	// in dieser Reihenfolge!
}

template<class T, class U>void limit(T&v,U min,U max) {
 if (v<min) v=min;
 if (v>max) v=max;
}

bool onSetReport(uint16_t wValue,uint16_t wLength) {
 union{
  RepF repF;
  RepLinak repLinak;
  RepReboot repReboot;
  byte repid;
  operator byte*() {return &repid;}
 }recvbuf;
 uint16_t len=0;
 switch (wValue) {
  case 0x0301: len=sizeof(RepF); break;
  case 0x0302: len=sizeof(RepLinak); break;
  case 0x022A:
  case 0x0363: len=sizeof(RepReboot); break;
 }
 if (!len) return false;		// falsches setup.wValue
 if (wLength!=len) return false;	// falsche Transaktionslänge
 bool ret=usb::recv(0,recvbuf,len);
 if (recvbuf.repid!=(wValue&0xFF)) return false;	// falsche Report-ID
 switch (wValue) {
  case 0x022A: /*if (recvbuf.repReboot.doit==1)*/ adc::manualtrigger(); break;
  case 0x0363: if (recvbuf.repReboot.doit==1) bootstart(); break;
  case 0x0301: {
   byte change=0;
   if (recvbuf.repF.flags<2
   && repF.flags!=recvbuf.repF.flags) {
    repF.flags=recvbuf.repF.flags;
    change|=0x10;
   }
// Einzelwertweise übertragen
   if ((unsigned)recvbuf.repF.brems<4096
   && repF.brems!=recvbuf.repF.brems) {
    repF.brems=recvbuf.repF.brems;
    change|=0x20;
   }
   if ((unsigned)recvbuf.repF.k1<4096	// muss >=0 sein!
   && repF.k1!=recvbuf.repF.k1) {repF.k1=recvbuf.repF.k1; change|=1;}
   if ((unsigned)recvbuf.repF.k2<4096	// muss >=0 sein!
   && repF.k2!=recvbuf.repF.k2) {repF.k2=recvbuf.repF.k2; change|=2;}
   if ((unsigned)recvbuf.repF.tg<=RepF::tg_max
   && repF.tg!=recvbuf.repF.tg) {repF.tg=recvbuf.repF.tg; change|=4;}
   if (recvbuf.repF.tp<=repF.tg	// hier: beliebig negativ erlaubt
   && repF.tp!=recvbuf.repF.tp) {repF.tp=recvbuf.repF.tp; change|=8;}

   if (change&0x30 && repF.flags&1) pwm::out(repF.brems);
   adc::reloadFeature(change&0x0F);
  }break;
 }
 return ret;
}

bool onGetReport(uint16_t wValue, uint16_t) {
 switch (wValue) {
  case 0x0101: return usb::send0(repI,sizeof repI);
  case 0x0102: return usb::send0(repStoß,sizeof repStoß);
  case 0x0301: return usb::send0(repF,sizeof repF);
  case 0x0302: return usb::send0(repLinak,sizeof repLinak);
 }
 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
 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;
}

void send2(const byte*report,unsigned len) {
// Während HID-Reports vom usbhid.sys ausgelesen werden, auch wenn keine Äpp
// sich dafür interessiert …
 usb::send(1,report,len);
// … sammeln sich Bulk-Daten bei WebUSB an, wenn niemand diese abholt.
// Daher nur bei freiem Puffer (= Zuhörer da) absenden
 if ((USB.EPR[2]&EPTX_STAT)==EP_TX_NAK) 
  usb::send(2,report,len);
// Da ergibt sich allerdings ein Problem mit Transfers > 64 Byte:
// Kann (und wird) mittendrin hängen bleiben, wenn sich die Web-Äpp verabschiedet:
// Timeout erforderlich
}

void idle() {
 static uint32_t 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&0x80) {
   adc::get_poti(repI.p);
   if (!(repF.flags&1)) pwm::out(repI.p.sum);	// echte Division!?
   repI.flags&=~1;
   if (!(GPIOA->IDR&1<<6)) repI.flags|=1;
   send2(repI,sizeof repI);
   repI.reset();
  }else if (repStoß.flags==(0x80|elemof(repStoß.kraft))) {
   send2(repStoß,sizeof repStoß);
   repStoß.reset();
  }
  BB_BIT(lock,1)=0;
 }
}

static void meminit() {
 extern uint32_t __ctors,__etext,__data,__bss,__noinit;
 auto s=&__etext, d=&__data;
 while (d!=&__bss)    *d++=*s++;	// initialize data
 while (d!=&__noinit) *d++=0;		// clear BSS
 s=&__ctors;				// list of static constructors
 while (s!=&__etext) reinterpret_cast<func_ptr_t>(*s++)();	// 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 = 0x88889888;	// A7:5 auf High ziehen, A3 = PWM-Ausgang
 GPIOA->CRH = 0x88854888;	// 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;		// Header initialisieren
 repStoß.repid=2;
 repLinak.repid=2;
 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
};
Vorgefundene Kodierung: UTF-80