/* A/D-Wandler des STM32F103
211014 erstellt
*/
#include "hips.h"
namespace adc{
static uint16_t dmabuf[256] __attribute__((section(".noinit")));
// DMA-Interrupts werden nur dazu benutzt,
// Statistiken über die Kraft zu erfassen.
// Die Oszi-Funktion verlässt sich auf den Analog-Watchdog,
// und von da ab wird die Historie ausgelesen.
// Letzteres schreit nach einem Speicher-zu-Speicher-DMA-Transfer
// und/oder nach einer Pufferumschaltung.
unsigned collect(int16_t*d,unsigned l) {
unsigned constexpr n=elemof(dmabuf);
unsigned idx=n-DMA1_Channel1->CNDTR;
if (l>n) l=n;
else if (l<n) {
idx+=n-l;
if (idx>=n) idx-=n; // unnötige ältere Einträge ignorieren
}
for (unsigned i=0; i<l; i++) {
d[i]=dmabuf[idx];
if (++idx>=n) idx=0;
}
return l;
}
// ISR, Aufruf nach halber und voller DMA-Puffer-Füllung
// Einzige Aufgabe: Statistikwerte sammeln
static void dma_end() {
const uint32_t isr=DMA1->ISR;
DMA1->IFCR=1; // Alle Arten von DMA-Interrupts Kanal 1 bestätigen
uint16_t const*p=dmabuf; // DMA zur Hälfte
if (isr&2) p+=elemof(dmabuf)/2;// DMA komplett
auto&r=repI; // Input-Report (aus später mehreren)
if (r.n==65535) return; // Nichts tun wenn Puffer fertig
r.k.add(p[0]); // Nur den 1. Wert wegen Rechenlast!
// r.k.add(p[32]);
if (++r.n>=65000) r.flags|=0xC0;
// Voll genug, Überlauf droht (weil Messschieber fehlt)
}
//ISR, Aufruf nach Ablauf einer Zeitspanne nach 2. Trigger
//TODO: Hohe Priorität!
static void watchT() {
TIM3->SR=0;
repStoß.collect();
}
static void future(func_ptr_t cb,int16_t to) {
vtbl[16+TIM3_IRQn]=cb;
TIM3->ARR = to>0 ? to-1 : 0; // Abzuwartende Samples
TIM3->CR1 = TIM_CR1_OPM|TIM_CR1_CEN;
}
static Simplestat poti;
static uint16_t poti_n;
uint16_t get_poti(Simplestat&p) {
ADC2->CR1 = ADC_CR1_SCAN; // Hier darf kein Interrupt vom ADC2 dazwischenfunken
p=poti; // memcpy
if (poti_n) p.sum/=poti_n;
else if (repI.flags&0x20) p.sum>>=16;
poti.reset();
poti_n=0;
ADC2->CR1 = ADC_CR1_SCAN|ADC_CR1_EOCIE; // Interruptfreigabe
return p.sum;
}
static uint32_t tr[6] __attribute__((section(".noinit")));
static uint32_t*trp; // Laufzeiger in obige Liste
static void advancetrigger() {
ADC1->SR=0; // Flags löschen
ADC1->HTR=*trp++; // Grenzen umaktivieren
ADC1->LTR=*trp++;
}
//ISR: Sammelinterrupt für ADC1: Watchdog und ADC2: Konversionsende
//TODO: Mittlere Priorität!
static void check_adc() {
if (ADC1->SR&ADC_SR_AWD) { // Watchdog = Triggergrenzenüberschreitung?
advancetrigger(); // Zweite Triggergrenze (Phase 2) oder keine (Phase 3)
byte f = repI.flags = repI.flags+4 | 0x80; // auf Phase 2 oder 3;
if ((f&0x0C)==0x0C) future(watchT,repF.tg-repF.tp); // Phase 3 erreicht
}
if (ADC2->SR&ADC_SR_EOC) {
uint16_t v = ADC2->DR; // der Lesezugriff löscht ADC_SR_EOC
auto&r=repI;
if (!(r.flags&0x20)) {
poti.add(v);
if (!++poti_n) r.flags|=0x20;
}
}
}
//ISR, Flankentrigger scharfmachen, Aufruf wenn Prätrigger-Zeit abgelaufen
//TODO: Hohe Priorität!
static void edgearm() {
TIM3->SR=0; // Timer3 erledigt
advancetrigger();
repI.flags = repI.flags+4 | 0x80; // Zustandsbits 3:2 auf Phase 1
}
void arm() {
repI.flags = repI.flags&~0x0C | 0x80; // Zustandsbits 3:2 auf Phase 0
if (repF.k1<=repF.k2) { // steigende Flanke suchen
tr[0]=0xFFF;
tr[1]=repF.k1; // Unterschreitung feststellen lassen
tr[2]=repF.k2; // Überschreitung feststellen lassen
tr[3]=0;
}else{
tr[0]=repF.k2; // Überschreitung feststellen lassen
tr[1]=0;
tr[2]=0xFFF;
tr[3]=repF.k1; // Unterschreitung feststellen lassen
}
tr[4]=0xFFF;
tr[5]=0;
trp=tr;
future(edgearm,repF.tp);
}
void manualtrigger() {
//PROBLEM!!
// if (NVIC->ISER[TIM3_IRQn>>5]&1<<(TIM3_IRQn&31)) return;
ADC1->HTR=0xFFF;
ADC1->LTR=0; // Triggerbedingungen beseitigen
repStoß.collect();
}
// Timer2: Periode 13 Hz, Periodendauer 76 ms
static void timerInit() {
TIM3->PSC = 6*14-1; // = 83, entsprechend 1,16 µs (Abtastrate von AIN8)
TIM3->DIER = TIM_DIER_UIE; // Interrupt wenn CNT==ARR -> CNT==0
NVIC_EnableIRQ(TIM3_IRQn);
}
void reloadFeature(byte change) {
if (change) arm(); // wenn die DMA-Puffergröße bleibt, nichts weiter tun
}
uint32_t getTriggerState() {
return (trp-tr)>>1; // 0..3
}
static void dmaInit() {
vtbl[16+DMA1_Channel1_IRQn]=dma_end;
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
//ADC1 ist fest mit DMA1Ch1 verknüpft!
DMA1_Channel1->CPAR = uint32_t(&ADC1->DR);
DMA1_Channel1->CMAR = uint32_t(dmabuf);
DMA1_Channel1->CNDTR = elemof(dmabuf);
DMA1_Channel1->CCR = 1<<10 // 16 Bit Speicher
|2<<8 // 32 Bit Peripherie (Seite 237: „must be accessed by words“)
|1<<7 // Speicher-Inkrement
|1<<5 // zirkulär
|1<<2 // Interrupt bei halber Transferlänge
|1<<1 // Interrupt bei voller Transferlänge
|1<<0; // Freigabe
}
// Die Portpins und RCC sind bereits initialisiert!
void init() {
ADC1->CR2 = ADC_CR2_ADON; // einschalten
ADC2->CR2 = ADC_CR2_ADON;
// Beide A/D-Wandler zuerst „kalibrieren“ (mit „genau“ hat das Ergebnis kaum zu tun)
delay(8); // 2 ADC-Takte warten: (48MHz / 12MHz) * 2 = 8 => 8 Nops
ADC1->CR2 = ADC_CR2_CAL|ADC_CR2_ADON; // Kalibrierung beider ADCs starten
ADC2->CR2 = ADC_CR2_CAL|ADC_CR2_ADON;
while ((ADC1->CR2|ADC2->CR2)&ADC_CR2_CAL); // Warten bis beide ADCs fertig
// ADC2 konvertiert ausschließlich das Potenziometer im Schneckentempo.
ADC2->SQR3 = 9<<0; // ADC-Kanäle der Normalsequenz: Nur ADC9
// ADC2->SQR1 = 1-1<<20;
ADC2->SMPR2 = 7<<27; // Maximale Abtastzeit für ADC9 (Potenziometer)
// Wandlungszeit: 239,5 + 12,5 = 252 ADC-Takte ≈ 48 kSa/s
ADC2->CR1 = ADC_CR1_SCAN|ADC_CR1_EOCIE;
ADC2->CR2 = ADC_CR2_CONT|ADC_CR2_ADON;
ADC2->SR = 0; // Eventuelle Interruptanforderungen löschen
// ADC1 konvertiert oszilloskopartig die Kraftmessdose mit minimaler Abtastzeit
// Wandlungszeit: 1,5 + 12,5 = 14 ADC-Takte ≈ 1,2 µs ≈ 857 kSa/s
ADC1->SQR3 = 8<<0;
// ADC1->SQR1 = 1-1<<20;
// Interrupt bei Analog-Watchdog, auf 1 Kanal bezogen.
// Die Schaltgrenzen (LTR, HTR) werden per Software gesteuert
// Nach Reset sind sie so eingestellt, dass keine Interrupts ausgelöst werden.
ADC1->CR1 = ADC_CR1_AWDEN|ADC_CR1_AWDSGL|ADC_CR1_SCAN|ADC_CR1_AWDIE|8;
// Scan mit Watchdog auf Takt; Dual-Modus: 6 = Regulär Simultan
ADC1->CR2 = ADC_CR2_DMA|ADC_CR2_CONT|ADC_CR2_ADON; // DMA, kontinuierlich, noch nicht loslegen
ADC1->SR = 0; // Eventuelle Interruptanforderungen löschen
dmaInit();
timerInit();
vtbl[16+ADC1_2_IRQn]=check_adc;
NVIC_EnableIRQ(ADC1_2_IRQn); // Ob ADC1 oder ADC2 wird in der ISR verzweigt
ADC2->CR2 = ADC_CR2_CONT|ADC_CR2_ADON;
ADC1->CR2 = ADC_CR2_DMA|ADC_CR2_CONT|ADC_CR2_ADON; // loslegen
arm();
}
}//namespace
Detected encoding: UTF-8 | 0
|