#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
};
| Detected encoding: UTF-8 | 0
|