Source file: /~heha/hs/bl/blink3.zip/main.cpp

// Lässt eine LED an PB12 blitzen.
// Nicht für BluePill (2× 20 Pins) sondern für ein kleineres Board mit 2× 17-poligen Pfosten
// 210916 heha

// CMSIS-„Bibliothek“: Registerdefinitionen.
// Wie alle Bibliotheken für 16- und 32-Bit-Mikrocontroller ist diese
// unglücklich gestrickt, was den Zugriff auf gleichartige Peripherie angeht:
// Statt GPIOA->reg, GPIOB->reg usw. wäre besser:
//	GPIOA.reg, GPIOB.reg … sowie GPIO[x].reg mit x von 0 ab.
// Sowie enum statt #define. Außerdem mehr C++.
// Bissel kleiner und mit hierarchischer Abhängigkeit statt aus einem Kodegenerator
// wäre ebenfalls nett.
#include <stm32f10x.h>
// CMSIS ist nicht in diesem Archiv, sondern in
// http://www.tu-chemnitz.de/~heha/hs/bl/cmsis3.zip/

// Berechnet zur Compilezeit die Bitadresse für das Register und das Bit
// Funktioniert dank Adresszerlegung sowohl für RAM als auch für Register
// (Eine constexpr-Funktion wird vom Compiler leider nicht immer inline
// platziert und ergibt dann größeren Kode.)
// WICHTIG: Die Adresse muss zur Compilezeit bekannt sein,
// sonst entsteht aufgeblähter Kode!
#define BB_BIT(a,b) (*(uint32_t*)(((uint32_t)&(a)&0xFF00'0000)+0x0200'0000+((uint32_t)&(a)&0x00FFF'FFFF)*32+(b)*4))

namespace bb{	// Bit-Banding = atomarer Bitzugriff, für RAM und Register

inline void set(volatile uint32_t&a, unsigned b, bool v=true) {
  BB_BIT(a,b) = v;
}
inline void clr(volatile uint32_t&a, unsigned b) {
  set(a,b,false);
}
inline bool test(const volatile uint32_t&a, unsigned b) {
  return (bool)BB_BIT(a,b);
}
}

// Eine einfache Warteschleife, die hier „geinlined“ wird
static void delay(uint32_t timeout) {
 do __NOP(); while(--timeout);
}

// Hier geht's los.
// Achtung! Statische Variablen sind weder initialisiert (.data) noch nullgesetzt (.bss)!!
// Statische Konstruktoren wurden nicht aufgerufen.
// Das macht normalerweise der Startupkode vor main().
void onReset() {
// Der auf dem Board fest installierte USB-Pullup hat den Nachteil,
// dass während des Festhaltens von RESET die USB-Enumerierung von Windows anläuft
// und dann folglich ein unresponsives, defektes Gerät meldet.
// Daher erscheint es sinnvoller, PA12 nach Low zu zerren und so ein
// nicht angeschlossenes USB-Gerät zu simulieren.
 bb::set(RCC->APB2ENR,2);	// PortA takten
 bb::set(GPIOA->CRH,16);	// Output-Modus PA12: Open Drain
 bb::clr(GPIOA->ODR,12);	// Löschen = USB+ nach Low = Disconnect
// Damit die Leuchtdiode an PB12 überhaupt angesprochen werden kann,
// muss der Takt für die Port-Peripherie aktiviert werden.
 bb::set(RCC->APB2ENR,3);	// PortB takten
// Dann erst lässt sich der Port als Ausgang festlegen.
// Für Open Drain (genügt für die nach Speispannung angeschlossene LED)
// genügt es ein einziges Bit zu setzen.
 bb::set(GPIOB->CRH,16);	// Output-Modus PB12: Open Drain
 for(;;) {
  bb::clr(GPIOB->ODR,12);	// Löschen = LED ein
  delay(0x0001'0000);
  bb::set(GPIOB->ODR,12);	// Setzen = LED aus
  delay(0x0007'0000);
 }
}
// ACHTUNG: PB12 ist die LED (nach Plus) am BlackPill-34-Board!
// Bei verbreiteteren BluePill ist die LED an PC13 (ebenfalls nach Plus),
// wie auch beim STM32F401-Board.

typedef void(*handler_t)();

// Um ohne Laufzeitbibliothek auszukommen
// wird die Interrupttabelle hier definiert (2 Einträge sind nötig)
// und vom Linker an die Anfangsadresse (hinter dem Urlader) platziert.
handler_t vectors[] __attribute__((section(".vectors"))) = {
 (handler_t) (SRAM_BASE + 20*1024),	// Startwert des Stapelzeigers
 onReset,	// Einsprungadresse bei RESET, hier: vom Urlader ausgewertet
};
Detected encoding: UTF-80