Source file: /~heha/mb-iwp/UZD1/Firmware.zip/main.cpp

/* Firmware für ATtiny24 auf Doppel-Digitalvoltmeter
   für UZD1 und vergleichbare Labornetzteile, Henrik Haftmann
 190528	erstellt

Hardware (nach Funktion sortiert):
13	PA0	ADC0	Analogeingang Spannung
12	PA1	ADC1	Analogeingang Strom
11	PA2	ADC2	frei
Charlieplex-Display		[L*]	[G]	[R*]
 2	PB0			-	K0	Aa	(links)
 3	PB1			Aa	K1	Ab
 5	PB2			Ab	K2	Ac
10	PA3			Ac	K3	Ad
 9	PA4	SCK	ISP	Ad	K4	Ae
 8	PA5	MISO	ISP	Ae	K5	Af
 7	PA6	MOSI	ISP	Af	K6	Ag
 6	PA7			Ag	K7	-	(rechts)

 4	PB3	RESET	ISP	
 1	Ucc		5P
14	GND		GND

[L*] = Segment links vom gemeinsamen Anschluss
[G]  = Gemeinsamer Anschluss (Katode)
[R*] = Segment rechts vom gemeinsamen Anschluss

Verwendung der Timer:
*/

#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>	// sei()
#include <avr/pgmspace.h>
#include <avr/fuse.h>		// todo
#include <avr/signature.h>

typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long dword;
#define NOINIT __attribute__((section(".noinit")))

static byte display[8] NOINIT;	// bereits für Charlieplex umgerechnete Ausgabedaten
const bool vqe21=true;	// Sonderbehandlung für 3½-stellige Anzeige
// (Bei gleicher Verdrahtung andere effektive Anschlussbelegung für VQE21)

// Siebensegmentkode ausgeben
static void out7seg(byte dl,byte pos) {
 byte dh=dl<<1;
 byte k=1<<pos;		// Katodenbit
 dl&=k-1;		// Bits unterhalb des Katodenbits, bei pos==0 wird dl=0
 dh&=~(byte(k<<1)-1);	// Bits oberhalb des Katodenbits, bei pos==7 wird dh=0
 dl|=dh;
 display[pos]=dl;	// anzeigen lassen
}

// Zeichen ausgeben; zurzeit nur 0..9 (nicht '0'..'9')
static void outchar(byte c,byte pos) {
 static const PROGMEM byte data7seg[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
 byte dl=0;
 if (c<10) {
  if (vqe21 && !(pos&3)) dl=0x0A;	// n darf hier nur 1 sein; Segmente „b“ und „d“ treiben
  else dl=pgm_read_byte(data7seg+c);
 }
 out7seg(dl,pos);
}

// Dezimalzahlausgabe von rechts nach links, mindestens 2-stellig
// value darf nicht größer als 9999 sein, mit VQE21 nicht größer als 1999
static void showVal(word value,byte pos) {
 char nk=1;		// Hier: Mindestens 2 Ziffern ausgeben
 word max=vqe21?1999:9999;
 if (value>max) value=max;
 do{			// Mindestens 1 Ziffer ausgeben
  byte rem=value%10;
  value/=10;		// Compiler optimiert beide Divisionen zu einer
  outchar(rem,--pos);
 }while(--nk>=0 || value);
 while (pos&3) outchar(-1,--pos);	// mit „Leerzeichen“ nach links auffüllen
}

static byte curKatode NOINIT;		// 0..7

// 1,2 kHz, ÷ 8 = 150 Hz Refresh-Rate
static void multiplex() {
 byte i=++curKatode&7;
 byte k=1<<i;		// Katodenbit
 i=display[i];		// Aktive Segmente; das Katodenbit wird dort 0 gehalten
 k|=i;			// Aktive Ausgänge (Katodenbit dazu)
 DDRA=DDRB=0;
 PORTB=i&7;PORTA=i&0xF8;// zu invertieren für gemeinsame Anode
 DDRB=k&7; DDRA=k&0xF8;
}

static dword adcSum[2] NOINIT;	// sammelt 10+8=18 Bit
static word a[2] NOINIT;	// Akkumulierte Analogwerte zum Skalieren und Ausgeben in der Hauptschleife

ISR(ADC_vect) {
 if (ADMUX&0x01) {
  ADMUX&=~0x01;
  adcSum[0]+=ADC;
  multiplex();
 }else{
  ADMUX|= 0x01;
  adcSum[1]+=ADC;
  if (!curKatode) {
   a[0]=adcSum[0]>>8; adcSum[0]=0;	// 16 der 18 Bits abgreifen (2 LSB verwerfen)
   a[1]=adcSum[1]>>8; adcSum[1]=0;
   GPIOR0|=0x80;			// Dem Hauptprogramm melden
  }
 }
}

const word CAL0=247;	// Endwertkalibrierung für A/D-Wandler, 1024 = 1:1
const word CAL1=1438;

int main() {
 for (byte i=0,m=1; i<8; i++,m<<=1) display[i]=~m;	// Lampentest
 ACSR |= 0x80;	// Analogvergleicher deaktivieren
 PRR   = 0x0E;	// Alles aus außer ADC
 DIDR0 = 0xFF;	// Gar keine Digitaleingänge!
 MCUCR = 0x68;	// Schlafmodus: ADC-Rauschunterdrückung; keine Pullups (= Flackereffekte beim Charlieplexen)
 ADMUX|= 0x80;	// Referenzspannung 1,1 V, Eingang ADC0
 ADCSRA= 0xFC;	// A/D-Wandler starten mit Abtastrate 1 MHz / 32 / 13 = 2,4 kSa/s
 ADCSRB|=0x10;	// linksbündig
 sei();
 for(;;) {
  sleep_cpu();
  if (GPIOR0&0x80) {	// 1,2 kHz ÷ 256 = 4,7 Hz Update-Rate
   showVal(((dword)a[0]*CAL0>>16)+3,4);	// skalieren und ausgeben: Spannung
   word w=(dword)a[1]*CAL1>>16;
   if (w) w--;		// Offset -1
   showVal(w,8);	// skalieren und ausgeben: Strom
   GPIOR0&=~0x80;
  }
 }
}
Detected encoding: UTF-80