#pragma once
#include <stm32f10x.h>
#include "usb.h"
/* hea = Hüftgelenkprothesen-Einschlag-Apparat
Teilprojekt im Virtual-Reality-Projekt HIPS:
Simulation der Chirurgie inklusive Haptik:
- Halten und Führen von Sägen und Raspeln (nicht hier)
- Einschlag der Prothese (hier)
Hardware STM32F103 (vermutlich China-Clone) auf BlackPill-Board:
A3 T2C4 PWM-Ausgang, steuert die Bremskraft
A4 ADC4 Hand-Potenziometer für manuelle Bremskraftvorgabe
A5 SCK1 Messschieber-Takt vom LM393 ¹
A6 (ADC6) Verdreh-Sensor (Mikrotaster) ¹
A7 MOSI1 Messschieber-Daten vom LM393 ¹
A9 (Tx1) Debug-Ausgang: ISR-Lastanzeige
A10 (Rx1) Debug-Ausgang: Idle-Lastanzeige
(A11 USB D-)
(A12 USB D+)
B0 ADC8 Kraftmessdose, ≥ 100 kSa/s
B1 ADC9 Weg-Potenziometer, ≥ 100 kSa/s
B10 TxD3 Interface der Linak-Steuerung
B11 RxD3 Interface der Linak-Steuerung
(B12 Blaue LED, low-aktiv)
¹ erfordert chip-internen Pullup
Verwendung der Peripherie:
ADC1 Kraftmessdose, Weg-Potenziometer (schnell und kontinuierlich)
ADC2 Hand-Potenziometer (gelegentlich)
USART3 Linak-Servo
SPI1 Messschieber
Timer2 PWM-Ausgang
Timer3 Triggerverzögerung für ADC1
Timer4 Endeerkennung für SPI1
DMA1CH1 ADC1 (Kraftmessdose)
DMA1CH2 SPI1 (Messschieber)
USB HID+WebUSB
*/
/***************************
* Grundlegende Funktionen *
***************************/
void basic_delay(uint32_t); // Wartet <argument×3>? Takte
void set_sysclock(); // Systemtakt auf 72 MHz (8-MHz-Quarz + PLL ×9) setzen
void revert_sysclock(); // Systemtakt auf ?? (RC-Oszillator) zurücksetzen
void idle();
#define BB_BIT(a,b) (*(volatile uint32_t*)(((uint32_t)&(a)&0xFF000000)+0x02000000+((uint32_t)&(a)&0x00FFFFFFF)*32+(b)*4))
/*************************
* Debug-Hilfsfunktionen *
*************************/
namespace debug{
inline void isrstart() {BB_BIT(GPIOA->ODR,9) = 1;}
inline void isrend() {BB_BIT(GPIOA->ODR,9) = 0;}
inline void workstart(){BB_BIT(GPIOA->ODR,10) = 1;}
inline void workend() {BB_BIT(GPIOA->ODR,10) = 0;}
}//namespace
namespace adc{
struct Stat;
}
// Alle Reports verwenden eine Report-ID.
// Ohne Konstruktor.
struct Report{
byte repid;
operator byte*() {return &repid;} // Als Ganzes ansprechen
operator const byte*() const {return &repid;}
struct Stat{ // Report::Stat, size: 8
int16_t
min, // Minimum (0..4095)
max, // Maximum (0..4095)
avg, // Mittelwert (0..4095)
σ; // MR610-Varianz = Standardabweichung der Grundgesamtheit
void operator<<(adc::Stat&); // „Raubkopie“
};
};
/*****************************
* A/D-Wandler und Statistik *
*****************************/
namespace adc{
void init();
enum{
loadF=1, // Triggerquelle, Triggerkopplung, Gesamt-Samplezahl
loadT=2, // Triggerschwelle, Hysterese
loadTp=4, // Prätrigger
loadTg=8, // Gesamt-Samplezahl
};
void loadFeature(byte); // bei Änderung von repF: Triggerschwellen nachführen
void arm();
void manualtrigger();
unsigned collect(uint16_t*,unsigned);
void get_allstat();
uint16_t get_poti(Report::Stat&); // liefert min, max, Mittelwert
}
extern struct RepF:public Report{ // size: 16
struct{byte
bk:2, // Bit 1:0 = Benutze Bremskraftvorgabe
// 00 = Bremskraft vom Potenzoimeter
// 01 = Bremskraft von <brems>
// 10 = undefiniert
// 11 = undefiniert
tk:2, // Bit 3:2 = Triggerkopplung
// 00 = DC
// 01 = AC (<t1> und <t2> verändern sich laufend)
// 10 = undefiniert
// 11 = undefiniert
tq:2, // Bit 5:4 = Triggerquelle
// 00 = PB0 ≡ ADC8 (Kraft)
// 01 = PB1 ≡ ADC9 (Wegpoti)
// 10 = PA6 ≡ ADC6 (Verdrehtaster?)
// 11 = PA1 ≡ ADC1 (frei)
lk:2; // Bit 7:6 = letzter Kanal des Oszilloskops, Kanalabtastrate reduzierend
// 00 = 1 Kanal ADC8
// 01 = 2 Kanäle (zurzeit fest) ADC8 + ADC9
// 10 = 3 Kanäle ADC8 + ADC9 + ADC6
// 11 = 4 Kanäle ADC8 + ADC9 + ADC6 + ADC1
operator byte&() {return *(byte*)this;} // im ganzen zugreifbar machen
}f; // Flags
uint16_t brems;// Bremskraftvorgabe, 0..4095
uint16_t tv; // Trigger-Vorbereitung, 0..4095, bei AC variabel
int16_t th; // Trigger-Hysterese, positiv = steigende Flanke
int16_t tp; // Prätrigger (negativ = Posttrigger, max. <tg>)
uint16_t tg; // Gesamtzeit in Samples, also 7/6 µs, durch 12 teilbar
uint16_t knull;// Kraft-Nullwert (wird bei Reset vom A/D-Wandler aktualisiert? Erstmal nicht!)
uint16_t knib; // Oszi-ADC-Kanalliste in 4 Nibbles
// static constexpr int16_t tg_max=500;
byte settrigger(int); // Trigger-Vorbereitung anpassen
}repF;
extern struct RepI:public Report{ // size: 50
byte flags; // Bits:
// 7 = Neues <p>,<os> nach 100 ms *
// 6 = RepF geändert *
// 5 = Neues vom Messschieber, neues <ms> *
// 4 = RepT geändert, neues <ks>,<Δw> nach Triggerereignis *
// 3:2 = Phase der Triggerung:
// 0 = Abwarten von <repF.tp> Samples (nur wenn repF.tp>0)
// 1 = Warten auf erste Triggerbedingung (Unterschreitung <repF.k1>)
// 2 = Warten auf zweite Triggerbedingung (Überschreitung <repF.k2>)
// 3 = Abwarten von <repF.tg-repF.tp> Samples
// 1 = frei
// 0 = Zustand des Verdreh-Tasters
void reset(); // * = Dieses Bits lösen Interrupt-Transfer aus und werden danach gelöscht
Stat p; // ADC2-Statistik: Hand-Poti
Stat os[4]; // ADC1-Statistik (Oszi-Statistik):
// [0] Kraft, [1] Wegmess-Poti, [2] Verdrehsensor, [3] frei
int16_t ms; // Messschieber
int16_t ks; // Gemessener Kraftstoß (Integral über „Huckel“, t = 1 ms)
int16_t km; // Kraft-Maximum bei der Integration
int16_t Δw; // Wegdifferenz vom Wegmess-Poti beim Kraftstoß (nach 4 ms)
}repI;
extern struct RepL:public Report{ // size: 4
byte addr; // Bit 7 = lesen
uint16_t value;
}repL;
extern struct RepT:public Report{ // size:
struct Info{
byte nk:3, // Anzahl Kanäle (1..4)
tt:1, // TrueTrigger: echt (true), von Hand (false)
nr:4; // Laufende Oszillogramm-Nummer
// operator byte&() {return *(byte*)this;} // im ganzen zugreifbar machen
}info;
uint16_t fill; // Anzahl Samples (pro Kanal, typ. voll)
uint16_t data[3432]; // Trace-Daten, verzahnt (3432 ≈ 4 ms)
// Der Report ist immerhin knapp 7 KByte groß!
void reset() {adc::arm();}
void grab(bool tt) {
const unsigned nk = repF.f.lk+1;
info.nk= nk;
info.tt = tt;
info.nr++;
fill = adc::collect(data,elemof(data))/nk;
// Nach verzögertem Trigger: Daten aus DMA-Puffer einsammeln
integrate(repF.f.tk?repI.os[0].avg:repF.knull);
BB_BIT(repI.flags,4) = 1; // außerdem normaler 30-Byte-Report
sendcount++;
}
unsigned byteLength() const {return 4+info.nk*sizeof*data*fill;}
void integrate(int mean) const; // verändert repI
static byte sendcount;
}repT;
static_assert(elemof(RepT::data)%12 == 0);
struct RepReboot:public Report{
byte doit; // Nur wenn 1
};
/* Simple function pointer type to call user program */
typedef void (*func_ptr_t)();
extern func_ptr_t vtbl[16+56];
namespace usb{
extern byte DevStatus; // Bit 0: SelfPower, Bit 1: RemoteWakeup
extern byte DevConfig;
void shutdown();
void init();
// Diese Routinen können blockieren und rufen dann idle()!
// Rekursion ist zu vermeiden! Ausnahme: sendXx() ist innerhalb poll() erlaubt.
void poll();
bool send0(const byte*,unsigned);
bool send(byte,const byte*,unsigned);
// Der letzte Parameter steuert ob Short-Packets okay sind, und ist ein Zeiger
// auf die empfangenen Bytes.
// Diese Routine blockiert und ruft idle()!
bool recv(byte,byte*,unsigned,unsigned* =nullptr);
enum{
//onBusySkip=0x00, // nach 5 ms raus mit false
onBusyWait=0x10, // nach 100 ms raus mit false (nur 1. Paket)
onBusySend=0x20, // nach 5 ms überschreiben (nur 1. Paket)
noZlp=0x40, // Kein Null-Byte-Paket am Ende senden
partial=0x80, // short-packet nicht absenden sondern (beim nächsten Aufruf) fortsetzen
};
// Callbacks
bool onSetReport(SetupPacket&);
bool onGetReport(SetupPacket&);
}
#define LED_INIT() BB_BIT(GPIOB->CRH,16) = 1 // output open drain
#define LED BB_BIT(GPIOB->ODR,12) // 0 = on, 1 = off
namespace caliper{
void init(); // SPI1 und DMA1CH2 aktivieren
}
namespace pwm{
void init();
inline void out(uint16_t v) {TIM2->CCR4=v;}
}
Detected encoding: UTF-8 | 0
|