• .
  • Willkommen im Forum!
  • Alles beim Alten...
  • Du hast kaum etwas verpasst ;-)
  • Jetzt noch sicherer mit HTTPS
Hallo, Gast! Anmelden Registrieren


Tiny Tone
#41
(24.01.2019, 08:23 PM)alfsch schrieb: >>Es könnte weiterhelfen mal den CPU-Clock per fuse bit auf die PLL zu legen.

Rolleyes

davon bin ich selbstredend ausgegangen.

bei 250kHz haste 4 us, im irq-service. damit er also nicht nur noch irq macht, muss er in 3us oder so, fertig sein.
das geht nur: wenn cpu core so hoch wie möglich taktet, also pll-> /4  -> 16 MHz .
+ optimal wenig Befehle dort sind
+ die register nicht alle automatisch vom compiler auf den stack geschoben werden , sondern selbst "verwaltet"; 
in Bascom ist das irgenwas mit " # nosave "
Wink

"nous verrons" (LouisXiV)
für nicht Franzosen: Schaumermal!
...mit der Lizenz zum Löten!
 
Reply
#42
Die Fuse-bits zu ändern ist in reichlich Frust ausgeartet: AVR8-burn-o-mat ist inzwischen broken, er verlangt JRE7, aber JRE8 wird installiert.
Den Entwickler hatte ich schon mal vor einer Weile angeschrieben - keine Reaktion.
Kann man also vergessen.
Bleibt noch Fuses setzen mit AVR-dude auf der Kommandozeile - mit der üblich nerdigen Doḱumentation in form der man-page.
Mikrocontroller.net war zu diesem Thema auch nur wenig hilfreich.
Wie es denn geht habe nach einigem googlen letztendlich doch noch heraus gefunden.
Jetzt erreicht der PWM-takt 250kHz, der Irq quält sich mit 195kHz ab.
D.h. 125kHz würden schon mal funktionieren.
Wie man nun die irq-Reaktion verkürzt - vmtl über Compiler-Einstellungen - weiß ich allerdings nicht.
btw - aktueller Stromverbrauch ist nun 11,2mA bei 3,6V - genau das, was ich vorab befürchtet hatte.
...mit der Lizenz zum Löten!
 
Reply
#43
Wenn ich es mir so recht überlege, ist der GCC eigentlich für ziemlich optimalen code bekannt. Und im irq müssen lade- und schiebe-Operationen durchgeführt werden. Die sicherlich über Register laufen - und diese Register müssen - zusammen mit dem stack-pointer - gepushed und gepopped werden. Da beißt die Maus keinen Faden ab Rolleyes. Von daher glaube ich nicht, dass an dieser Stelle noch was zu holen ist. 250kHz-irq ist für diesen MCU-Winzling nun mal "sportlich".
...mit der Lizenz zum Löten!
 
Reply
#44
jo, gcc optimiert schon gut. aber bei sowas...ist selbst/assembler , mit zb nur 2 registern benutzt, die gesichert werden müssen, kaum von nem compiler zu erwarten.

+
mach doch, so wie es nu ist, auf 125khz rate. ist ja auch noch gut ausreichend , um bis 20k Ton zu erzeugen.
+
wenn pll in low power mode bringst, macht es 32 MHz und somit ist die volle pckl gleich passend für 125khz rate...und verbraucht weniger Saft.
ob der core dann noch auf 16MHz laufen kann -was er hier ja sollte- hab ich jetzt nich im Kopf...muttu DB befragen  Rolleyes

hier ein Beispiel: DDS in asm, timer INT --- läuft mit 256kHz rate problemlos ( im sinus sind es rund 16 Befehle, bei 16MHZ sind die in < 2us durch...)
+ es wird nur sreg , 1 Register benutzt ! und push/pop gesichert. DAS macht der compiler sicher nicht so...

Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

timer:
       in sregsave, SREG                 ; Statusregister sichern
       
; Sägezahn und Rechteck bekommen eine Sonderbehandlung:
       cpi waveform, t_square            ; ist Rechteck eingestellt?
       breq square                       ; wenn ja, springe zu square
       cpi waveform, t_sawtooth          ; ist Sägezahn eingestellt?
       breq sawtooth                     ; wenn ja, springe zu sawtooth

;Bei Sinus oder Dreieck wird einfach der Wert aus dem Speicher gelesen
       lpm                               ; Funktionswert aus dem Speicher lesen
       add z1, change1                   ; Phasenakkumulator
       adc z2, change2
       adc z3, change3
       out PORTD, R0                     ; Wert an den Analog-Digital-Wandler ausgeben
       out SREG, sregsave                ; Statusregister wiederherstellen
       reti                              ; Interrupt verlassen, zurück zum Hauptprogramm
       
square:                                   ; falls Rechteck:
       add z1, change1                   ; Phasenakumulator
       adc z2, change2
       adc z3, change3
       cpi z3, 128                       ; Wenn die Mitte einer Schwingungsperiode
                                         ; überschritten ist...
       brlo square_1
       out PORTD, null                   ; ...dann minimale Ausgangsspannung (0)
       out SREG, sregsave                ; Statusregister wiederherstellen
       reti                              ; Interrupt verlassen, zurück zum Hauptprogramm
square_1:
       out PORTD, full                   ; ...andernfalls maximale Ausgangsspannung
       out SREG, sregsave                ; (siehe oben)
       reti                              ;

sawtooth:
       add z1, change1                   ; Phasenakkumulator
       adc z2, change2
       adc z3, change3
       out PORTD, z3                     ; Beim Sägezahn ist die Spannung proportional zur
                                         ; Position in der Phase, das höchstwertige Byte
                                         ; der Position kann also direkt an den AD-Wandler
                                         ; gegeben werden
       out SREG, sregsave
       reti
    Don't worry about getting older.  You're still gonna do dump stuff...only slower
 
Reply
#45
Ja, das wird wohl bis auf Weiteres auf 125kHz hinaus laufen.
Was meinst Du mit low-power PLL-mode?
So sieht nun die bisherige low-power 1kHz-Version im Gehäuse aus:

   
   

Mechanische Probleme gab es mit der Bauhöhe der Batterie. Um in das Gehäuse zu passen, mußte ich den Boden des Batteriehalters ausbrechen.
Für den rückwärtigen Schalter außerdem eine Ecke aus der Platine sägen. Dann passt das alles.
...mit der Lizenz zum Löten!
 
Reply
#46
Aktueller Stand: habe mit 125kHz-PWM testweise bis 16kHz Sinus ausgegeben - das funzt, wobei 16khz schon etwas verbeult aussieht.
Sobald ich im Hauptprogramm den ADC wandeln lasse, ist es Essig mit der PWM-Ausgabe, so dass ich keine Möglichkeit habe die Frequenz per Poti aus zu wählen.
Bin dann erstmal offline, komplexe Tongemische konsumieren, produziert von realen menschlichen Wesen. Wink
...mit der Lizenz zum Löten!
 
Reply
#47
Fehler gefunden: Die AD-Wandlung hing in einer Endlosschleife weil...
ich in der Initialisierung im PRR-register den ADC abgeschaltet hatte aufsmaul
Die Tabellenwerte sind jetzt im 2-Oktav-Abstand besetzt:
4-16-63-250-1k-4k-16kHz. Reicht das?
...mit der Lizenz zum Löten!
 
Reply
#48
Zeig mal dein Programm...
Ich habe die tiny85 gefunden...da kann ich dann mal mitreden, sozusagen.
Wink
    Don't worry about getting older.  You're still gonna do dump stuff...only slower
 
Reply
#49
Code:
/* A Very Basic USB/LiIon-Battery-Powered Audio Sine-Wave Test Generator

!!BUILDING SITE - MAJOR WORKS AHEAD!!

created: 2014-01-21
update: 2019-01-26
author: VoltWide

Fuse Extended Byte
7 - 1 -
6 - 1 -
5 - 1 -
4 - 1 -
3 - 1 -
2 - 1 -
1 - 1 -
0 - 1 SELFPRGEN   self-programming enabled
Fuse High Byte
7 - 1 RSTDISBL    ext reset disabled
6 - 1 DWEN        DebugWire enabled
5 - 0 SPIEN       Serial program and data download
4 - 1 WDTON       watchdog timer always on
3 - 1 EESAVE      EEPROM preserves chip erase
2 - 1 BODLEVEL2   Brown-out detector trigger level
1 - 1 BODLEVEL1   Brown-out detector trigger level
0 - 1 BODLEVEL0   Brown-out detector trigger level

Fuse Low Byte

7 - 0 CKDIV8      Clock divided by 8           modify   1
6 - 1 CKOUT       Clock output enabled         default  1
5 - 1 SUT1        Start-up slowly rising Vcc   default  1
4 - 0 SUT0        Start-up slowly rising Vcc   default  0
3 - 0 CKSEL3      CPU-Clock 16MHz              default  0
2 - 0 CKSEL2      CPU-Clock 16MHz              default  0
1 - 1 CKSEL1      CPU-Clock 16MHz              modify   0
0 - 0 CKSEL0      CPU-Clock 16MHz              modify   1
----------------------------------------------------------
                                               lfuse=0xe1

DIP8/SOIC8 multilevel-pinning:
pin1 PB5 PCINT5 /RESET ADC0 dW
pin2 PB3 PCINT3 XTAL1 CLKI /OC1B ADC3
pin3 PB4 PCINT4 XTAL2 CLKO OC1B ADC2
pin4 GND
pin5 PB0 MOSI DI SDA AIN0 OC0A /OC1A AREF PCINT0
pin6 PB1 MISO DO AIN1 OC0B OC1A PCINT1
pin7 PB2 SCK USCK SCL ADC1 T0 INT0 PCINT2
pin8 VCC
*/

#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/eeprom.h>
#include <avr/wdt.h>
#include <util/delay.h>

const __flash char SineTab[] = {                        // 8bit sinewave lookup table
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
};
const __flash uint16_t FqTab[] = {2,8,32,128,512,2048,8192};    //2019-01-25: 4-16-63-250-1k-4k-16k

const int8_t FrequIdxMax = 6;                                   //2018-12-27

// i/o mapping
#define pin1 (1<<PB5)
#define pin2 (1<<PB3)
#define pin3 (1<<PB4)
#define pin5 (1<<PB0)
#define pin6 (1<<PB1)
#define pin7 (1<<PB2)

#define OutPwmMSB pin6
#define OutLSB pin3
#define OutChargeReq pin5
#define OutPoti pin7
#define InVcc pin2
#define InPoti pin3
//input divider Rhi/Rlo=10k/2k2 Cin=0 Vref=1V1 VBattMin=3V VBattMax=4V:
//Vin(max,min) = 0.72/0.54V
//ADCin(maxs,min) =167/125
#define VBattMax 167
#define VBattHyst 5
#define EmptyBattCountDown 200
//!!! 2count/sec:
#define WDTirqCountDown 3600

//++++++++ global vars

volatile uint16_t Phase16,PhaseOfs16,WDTirqCount;               // 2019-01-06
uint8_t PotiResult,VccResult,EmptyBattcount,FrequIdx;    //2018-12-27

ISR(TIM1_OVF_vect) {                                            // 2019-01-20 DDS-irq: output next sound sample
   PORTB |= OutChargeReq; //!!!
   Phase16 += PhaseOfs16;                                      // 2019-01-23 16bit phase pointer
   OCR1A = (SineTab[(Phase16 >> 8)]);                          // 2019-01-23 update pwm dutoy cycle
   PORTB &= ~OutChargeReq; //!!!
}
void Boot(void){
   //Timer
   PLLCSR = 1<<PLLE;                                           // 2019-01-18 start PLL: Icc=2.49mA*3V6
   //wait 100us!!!
   TCCR1 = (1<<PWM1A) | (1<<COM1A1) | (1<<CS11);               // 2019-01-23 OC1A=MSB, PCK=64Mhz/2!!!->irq_rate=125kHz
   GTCCR = (1<<TSM) | (1<<PWM1B) | (1<<COM1B1) | (1<<PSR1);    // 2019-01-20 timer1: stop, OC1B=LSB, reset pre-scaler
   OCR1C = -1;                                                 // 2019-01-20 max pwm cycle
   TCNT1 = 0;                                                  // 2019-01-20  clear counter
   OCR1A = 12;                                                // 2019-01-20  MSB=analog zero!!!
   OCR1B = 128;                                                // 2019-01-20  LSB=analog zero
   TIMSK = (1<<TOIE1);                                         // 2019-01-20  timer1-irq on counter overflow
   //Ports
   MCUCR |= (1<<PUD);                                          // 2018-12-26 pullup,sleep=off, sleepmode=idle
   DDRB = OutPwmMSB | OutChargeReq | OutPoti;                  // 2019-01-24 output ports
   PINB = -1;                                                  // 2018-12-26 power reduction of dig input buffers
   DIDR0 = (1<<ADC0D) | (1<<ADC1D) | (1<<ADC2D) | (1<<ADC3D);  // 2018-12-26 dig inputs=off
   PORTB = OutPoti;                                            // 2019-01-25 OutchargRequ=lo
   //ADC
   ADMUX = (1<<ADLAR) | (1<<MUX1);                             // 2019-01-26
   ACSR = (1<<ACD);                                            // 2018-12-26 comp=off
   ADCSRA = 0;                                                 // 2018-12-26 ADCen,ADCstart,ADCtrig,ADCirq=off, adcclk=cpuclk/2
   ADCSRB = 0;                                                 // 2018-12-26 unipolar, polarity=tru,free running
   //Misc
   PRR = (1<<PRTIM0) | (1<<PRUSI);                             // 2019-01-26 power timer0,usi=off
   PhaseOfs16 = 0;                                             // 2019-01-23
   Phase16 = 0;
   WDTirqCount = WDTirqCountDown;                              // 2019-01-06
   while (!( PLLCSR & (1<<PLOCK)));                            // 2019-01-24 polling lock PLL
   GTCCR &= ~(1<<TSM);                                         // 2019-01-20 start counter(s)
   PLLCSR = (1<<PLLE) | (1<<PCKE);                             // 2019-01-18 link PLL-> timer1 clk
  }
void GetPoti (void) {
   ADCSRA |= (1<<ADEN) | (1<<ADSC);                            // 2018-12-26 start ad-conversion fsample=clk/2 adcirq=off
   while (0 == (ADCSRA & (1<<ADIF)));                          // A/D-conversion
   ADCSRA |= (1<<ADIF);                                         // 2019-01-07
   PotiResult = ADCH;                                          // bit9..2 , 8bit result
}
void GetFrequ (void) {                                          // 2018-12-26
   FrequIdx = PotiResult>>5;                                   // 2018-12-26
   if (FrequIdx>FrequIdxMax)  FrequIdx=FrequIdxMax;            // 2018-12-27
   PhaseOfs16 = FqTab[FrequIdx];                               // 2018-12-26
}
ISR(WDT_vect){                                                  // ca 2x/s: autoclears WDIE, must be set again to avoid wdt-reset
   sleep_disable();                                            // wakeup!
   WDTirqCount--;                                              // 2019-01-07 shutdown not here, but in main loop
   WDTCR |= (1<<WDIE);                                         // continue wdt irq, not wdt reset
}
void WDTon(void) {                                              // 2019-01-06 p.45, safety level 1
   WDTCR = (1<<WDIE) | (1<<WDE) | (1<<WDP2) | (1<<WDP0);       // Watchdog timer 0.5sec ticker
   MCUSR |= (1<<WDRF);                                          // 2019-01-07 overrides WDE
}
void WDToff(void) {                                             // 2019-01-06 p.45, safety level 1, timed sequence:
   MCUSR = 0;                                                  // c.code example 8.4.2: clear WDRF
   WDTCR |= (1<<WDCE) | (1<<WDE);
   WDTCR = 0;                                                  // clear WDE
}
//++++++++ Main Program

int main(void) {
   Boot();
   sei();
   for (;;) {
   GetPoti();
   GetFrequ();
   //!!!GetVcc();
   //!!!if (WDTirqCount==0) ShutDown();                             // 2019-01-06
   }
}
/*
changelog ******************************************************

2019-01-06b
GetVcc: UVLO shutdown removed
cpu-load minimized
2019-01-18  PLLclock -> timer1 enabled
2019-01-23 disabled: Timer0, GetVcc, sleep
sineoutput with 16bit phase offset = 256:
61/122/245/357/357Hz @clk div 16/8/4/2/1
PCK/4 works: 245*256*256*4=64MHz & PWMclk=62.7kHz with 62.5kHz measured
PCLK/2 & /1 results in PWMclkmax=125kHz
2019-01-24
fusing the attiny
AVR8-Burn-O-Mat is broken, requires JRE7, but JRE8 is installed
fusing LFuse to E1 with AVRDude immediately without data file works like this:
/usr/bin/avrdude -P usb -c usbtiny -p t25 -U lfuse:w:0xe1:m
Timer1-Clk=32MHz -> fpwm=125kHz
MCU-Clk=16MHz    -> firq=125kHz
SineFreqTable: 4-16-63-250-1k-4k-16kHz
added GetPoti(), GetFrequ()
2019-01-26
bugfix: ADC-conversion stayed in endless loop: PRR had turned off ADC power!
no sleep  current consumption Icc=11.2mA @3V6 &10k pots
main idle current consumption Icc=10.2mA @3V6 &10k pots
output-filter is 4.7mH 10nF
(26.01.2019, 12:46 PM)alfsch schrieb: Zeig mal dein Programm...
Ich habe die tiny85 gefunden...da kann ich dann mal mitreden, sozusagen.
Wink
...mit der Lizenz zum Löten!
 
Reply
#50
HW-Mods:
FreqPoti = 100klin (ist jetzt permanent eingeschaltet)
Ausgangsfilter = 4.7mH 10nF als Anhaltspunkt
Batterie-Messteiler = 100k & 22k
...mit der Lizenz zum Löten!
 
Reply
#51
Mit etwas besserer Filterung lassen sich die 16kHz einigermaßen ausbeulen. Eine leichte asymmetrische Verzerrung ist jedoch zu erkennen.
Möglicherweise ist diese zurückzuführen auf die nicht phasenkorrekte PWM.
Derzeitiger Ausgangsfilter: 4,7mH zwischen port-Ausgang und Poti-Eingang, 10nF parallell zum Poti, 12nF parallel zur Ausgangsbuchse, hinter dem 1k0-Auskoppel-Widerstand.
Hier nun einige Schnappschüsse im 3,6V-Batteriebetrieb:

   
   
   
   


Angehängte Dateien Thumbnail(s)
   
...mit der Lizenz zum Löten!
 
Reply
#52
Dazu nun THD-Spektren, gemessen mit ARTA & EMU Tracker Pre
   
   
   
   
Hier ist k2 dominant (asymm Verzerrung). In der "lowpower"-Version ist dagegen k3 dominant.
Diesen Unterschied führe ich auf die phasenkorrekte PWM zurück.
...mit der Lizenz zum Löten!
 
Reply
#53
perfekt ! das ist praktisch 8 bit mit perfekter Auflösung.  ThumbUp
    Don't worry about getting older.  You're still gonna do dump stuff...only slower
 
Reply
#54
(27.01.2019, 02:38 PM)alfsch schrieb: perfekt ! das ist praktisch 8 bit mit perfekter Auflösung.  ThumbUp
Es kommt offenbar dem THD zugute, wenn man Phasenoffsets der Form 2^N wählt innerhalb des 510 Takte PWM-Zyklus.
Des weiteren verschlechtert sich der THD bei hohen Frequenzen wenn der Filter-Kondensator zu groß wird.
Der Ausgangsfilter hat ja eine Resonanz oberhalb der Tonfrequenz und stellt somit eine kapazitive Last dar.
Bei meiner LoPower-Version waren da ursprünglich 100nF hinter der Drossel. Jetzt sind es 22nF, dafür 100nF hinter dem Serienwiderstand (jetzt 3k3).
Damit messe ich nun die low power Version:    
15Hz/0.30%
30Hz/0.26%
61Hz/0.21%
122Hz/0.16%
244Hz/0.25%
490Hz/0.64%
980Hz/0.70%
...mit der Lizenz zum Löten!
 
Reply
#55
HW-bug entdeckt: Die Zuordnung der Beschriftung "SPI" und "WDT" des DIP-Swi ist vertauscht!
Bei irrtümlich geschlossenem SPI-Schalter und eingelegter Batterie wird letztere dann beim Programmieren mit den 5V vom SPI-Port verbunden und auf diese Weise abgekocht.
...mit der Lizenz zum Löten!
 
Reply
#56
Derzeitiger Ausgangsfilter für die schnelle Version: 4,7mH zwischen port-Ausgang und Poti-Eingang, 10nF parallell zum Poti, 4,7+1nF parallel zur Ausgangsbuchse, hinter dem 2k7-Auskoppel-Widerstand. Dies erbringt noch einmal eine leichte Verringerung des THD am oberen Bandende.
...mit der Lizenz zum Löten!
 
Reply
#57
Um den Stromhunger etwas einzudämmen, würde ich gerne das Hauptprogramm nur mit dem watchdog-irq aufrufen und ansonsten schlafen legen wollen (idle).
Da funkt mir aberTimer1 zwischen und reißt die CPU nach spätesten 10us aus dem Schlaf - und dann bringt das garnichts.
Klar, Timer1Irq ist eine Weckquelle. Und nu?
Habe an den Schlus des Timer1Irq mal ein sleep commando gesetzt - aber danach schläft wirklich alles, auch der Timer.
Hat jemand ne zündende Idee das Aufwecken durch Timer1Irq zu unterbinden?
Code:
/* A Very Basic USB/LiIon-Battery-Powered Audio Sine-Wave Test Generator, fast version

created: 2014-01-21
update: 2019-01-28
author: VoltWide
target                                                       ATTINY25-20
mcu clock frequency           ...............                64/4Mhz PLL

prototype data
output function                                              pwm sine
max output level, peak-peak                                  >90% of Vsupply
pwm sample rate                                              32MHz/256 = 125khz
pwm mode.....................................................simple, not phase correct
THD vs output frequencies                                    0.34% @15Hz
                                                             0.21% @60Hz
                                                             0.27% @250Hz
                                                             0.98% @1kHz
                                                             3.11% @4kHz
power supply                                                 2.7~5.5V: USB, 3V-LiBatt, 2~3xAA..
power consumption active, no load,                           3.6V/10.4~10.6mA
eof battery charge threshold = watchdog disable              4V
UVLO shut down threshold                                     disabled
shut down current consumption                                <1uA

Conditional WDT timeout shutdown in case Vcc < 4V.
Switch off and on again to restart.
Automatic permanent operation when fed by 5V supply.
Conditional WDT timeout shutdown disabled per DIP-switch.
Unconditional WDT-timeout shutdown by masking GetVcc(), ignoring DIP switch setting.
Vcc is measured across frequ poti (permanent on) against Ref1V1.

Fuse Extended Byte
7 - 1 -
6 - 1 -
5 - 1 -
4 - 1 -
3 - 1 -
2 - 1 -
1 - 1 -
0 - 1 SELFPRGEN   self-programming enabled
Fuse High Byte
7 - 1 RSTDISBL    ext reset disabled
6 - 1 DWEN        DebugWire enabled
5 - 0 SPIEN       Serial program and data download
4 - 1 WDTON       watchdog timer always on
3 - 1 EESAVE      EEPROM preserves chip erase
2 - 1 BODLEVEL2   Brown-out detector trigger level
1 - 1 BODLEVEL1   Brown-out detector trigger level
0 - 1 BODLEVEL0   Brown-out detector trigger level

Fuse Low Byte

7 - 0 CKDIV8      Clock divided by 8           modify   1
6 - 1 CKOUT       Clock output enabled         default  1
5 - 1 SUT1        Start-up slowly rising Vcc   default  1
4 - 0 SUT0        Start-up slowly rising Vcc   default  0
3 - 0 CKSEL3      CPU-Clock 16MHz              default  0
2 - 0 CKSEL2      CPU-Clock 16MHz              default  0
1 - 1 CKSEL1      CPU-Clock 16MHz              modify   0
0 - 0 CKSEL0      CPU-Clock 16MHz              modify   1
----------------------------------------------------------
                                               lfuse=0xe1

DIP8/SOIC8 multilevel-pinning:
pin1 PB5 PCINT5 /RESET ADC0 dW
pin2 PB3 PCINT3 XTAL1 CLKI /OC1B ADC3
pin3 PB4 PCINT4 XTAL2 CLKO OC1B ADC2
pin4 GND
pin5 PB0 MOSI DI SDA AIN0 OC0A /OC1A AREF PCINT0
pin6 PB1 MISO DO AIN1 OC0B OC1A PCINT1
pin7 PB2 SCK USCK SCL ADC1 T0 INT0 PCINT2
pin8 VCC
*/

#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/eeprom.h>
#include <avr/wdt.h>
#include <util/delay.h>

const __flash char SineTab[] = {                        // 8bit sinewave lookup table
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
};
const __flash uint16_t FqTab[] = {2,8,32,128,512,2048,8192};    //2019-01-25: 4-16-63-250-1k-4k-16k

const int8_t FrequIdxMax = 6;                                   //2018-12-27

// i/o mapping
#define pin1 (1<<PB5)
#define pin2 (1<<PB3)
#define pin3 (1<<PB4)
#define pin5 (1<<PB0)
#define pin6 (1<<PB1)
#define pin7 (1<<PB2)

#define OutPwmMSB pin6
#define OutLSB pin3
#define OutChargeReq pin5
#define OutPoti pin7
#define InVcc pin2
#define InPoti pin3
//input divider Rhi/Rlo=10k/2k2 Cin=0 Vref=1V1 VBattMin=3V VBattMax=4V:
//Vin(max,min) = 0.72/0.54V
//ADCin(maxs,min) =167/125
#define VBattMax 167
#define VBattHyst 5
//1count/64msec - 10000/Min:!!!
#define WDTirqCountDown 1200

//++++++++ global vars

volatile uint16_t Phase16,PhaseOfs16,WDTirqCount;               // 2019-01-06
uint8_t PotiResult,VccResult,EmptyBattcount,FrequIdx;    //2018-12-27

ISR(TIM1_OVF_vect) {                                            // 2019-01-20 DDS-irq: output next sound sample, wakes up idle mcu!
   Phase16 += PhaseOfs16;                                      // 2019-01-23 16bit phase pointer
   OCR1A = (SineTab[(Phase16 >> 8)]);                          // 2019-01-23 update pwm dutoy cycle
   //sleep_enable();                                             // 2018-12-26!!!
   //sleep_cpu();                                                // prevent wake-up here!!!
}
ISR(WDT_vect){                                                  // ca 2x/s: autoclears WDIE, must be set again to avoid wdt-reset
   sleep_disable();                                            // wakeup
   WDTirqCount--;                                              // 2019-01-07 shutdown not here, but in main loop
   WDTCR |= (1<<WDIE);                                         // continue wdt irq, not wdt reset
}
void Boot(void){
   //Timer
   PLLCSR = 1<<PLLE;                                           // 2019-01-18 start PLL
   //wait 100us!!
   TCCR1 = (1<<PWM1A) | (1<<COM1A1) | (1<<CS11);               // 2019-01-23 OC1A=MSB, PCK=64Mhz/2->irq_rate=125kHz
   GTCCR = (1<<TSM) | (1<<PWM1B) | (1<<COM1B1) | (1<<PSR1);    // 2019-01-20 timer1: stop, OC1B=LSB, reset pre-scaler
   OCR1C = -1;                                                 // 2019-01-20 max pwm cycle
   TCNT1 = 0;                                                  // 2019-01-20  clear counter
   OCR1A = 60;                                                // 2019-01-20  MSB=analog zero!!
   OCR1B = 128;                                                // 2019-01-20  LSB=analog zero
   TIMSK = (1<<TOIE1);                                         // 2019-01-20  timer1-irq on counter overflow
   //Ports
   MCUCR |= (1<<PUD);                                          // 2018-12-26 pullup,sleep=off, sleepmode=idle
   DDRB = OutPwmMSB | OutChargeReq | OutPoti;                  // 2019-01-24 output ports
   PINB = -1;                                                  // 2018-12-26 power reduction of dig input buffers
   DIDR0 = (1<<ADC0D) | (1<<ADC1D) | (1<<ADC2D) | (1<<ADC3D);  // 2018-12-26 dig inputs=off
   PORTB = OutPoti;                                            // 2019-01-25 OutchargRequ=lo
   //ADC
   ADMUX = (1<<ADLAR) | (1<<MUX1);                             // 2019-01-26
   ACSR = (1<<ACD);                                            // 2018-12-26 comp=off
   ADCSRA = 0;                                                 // 2018-12-26 ADCen,ADCstart,ADCtrig,ADCirq=off, adcclk=cpuclk/2
   ADCSRB = 0;                                                 // 2018-12-26 unipolar, polarity=tru,free running
   //Misc
   PRR = (1<<PRTIM0) | (1<<PRUSI);                             // 2019-01-26 power timer0,usi=off
   PhaseOfs16 = 0;                                             // 2019-01-23
   Phase16 = 0;
   WDTirqCount = WDTirqCountDown;                              // 2019-01-06
   WDTCR = (1<<WDIE) | (1<<WDE) | (1<<WDP1);                   // 2019-01-28 Watchdog timer 64msec ticker
   MCUSR |= (1<<WDRF);                                         // 2019-01-27 overrides WDE
   set_sleep_mode(SLEEP_MODE_IDLE);                            // 2019-01-28
   sleep_enable();                                             // 2019-01-28
   while (!( PLLCSR & (1<<PLOCK)));                            // 2019-01-24 polling lock PLL
   GTCCR &= ~(1<<TSM);                                         // 2019-01-20 start counter(s)
   PLLCSR = (1<<PLLE) | (1<<PCKE);                             // 2019-01-18 link PLL-> timer1 clk
  }
void ShutDown(void) {
   cli();                                                      // 2018-12-26
   ADCSRA = 0;                                                 // 2018-12-26 dis ADC???
   TCCR1 = 0;                                                  // 2019-01-27 disconn sine port from timer, PRIOR! to powersave
   MCUSR = 0;                                                  // 2019-01-06 clear WDRF
   WDTCR |= (1<<WDCE) | (1<<WDE);                              // 2019-01-06
   WDTCR = 0;                                                  // 2019-01-06 clear WDE
   ADMUX = 0;                                                  // 2019-01-06 disable ref1V1
   PRR = (1<<PRTIM0) | (1<<PRTIM1) | (1<<PRUSI) | (1<<PRADC);  // 2018-12-26 power timer0+1,usi,adc=off
   PORTB = 0;                                                  // 2018-12-26 LED=off
   set_sleep_mode(SLEEP_MODE_PWR_DOWN);                        // 2018-12-26
   sleep_enable();                                             // 2018-12-26
   //sleep_cpu();                                                // shut down!!!
}
void GetPoti (void) {
   ADCSRA |= (1<<ADEN) | (1<<ADSC);                            // 2018-12-26 start ad-conversion fsample=clk/2 adcirq=off
   while (0 == (ADCSRA & (1<<ADIF)));                          // A/D-conversion
   ADCSRA |= (1<<ADIF);                                         // 2019-01-07
   PotiResult = ADCH;                                          // bit9..2 , 8bit result
}
void GetFrequ (void) {                                          // 2018-12-26
   FrequIdx = PotiResult>>5;                                   // 2018-12-26
   if (FrequIdx>FrequIdxMax)  FrequIdx=FrequIdxMax;            // 2018-12-27
   PhaseOfs16 = FqTab[FrequIdx];                               // 2018-12-26
}
void GetVcc (void) {
   ADCSRA |= (1<<ADEN) | (1<<ADSC);                            //2019-01-06 start adc fsample=clk/2 adcirq=off
   while (0 == (ADCSRA & (1<<ADIF)));                          //
   ADCSRA |= (1<<ADIF);                                        // 2019-01-07
   VccResult = ADCH;                                           // bit9..2 , 8bit result
   if (VccResult>=VBattMax) WDTirqCount++;                     // 2019-01-27
   if (WDTirqCount>WDTirqCountDown) WDTirqCount = WDTirqCountDown; // 2019-01-28 limited  countup
}
//++++++++ Main Program

int main(void) {
   Boot();
   sei();
   for (;;) {                                                  // 2019-01-27 1xloop / 64msec WDTirq
       PRR &= ~(1<<PRADC);                                     // 2019-01-28 wakeup i/o
       PORTB |= OutPoti;                                       // 2019-01-28 wakeup i/o
       GetPoti();
       ADMUX = (1<<ADLAR) | (1<<REFS1) | (1<<MUX0) | (1<<MUX1);// 2019-01-27 adcref=1V1, result left adj, in=ADC3/PB3/pin2
       GetFrequ();                                             // 2019-01.26
       GetVcc();                                               // 2019-01-27
       ADMUX = (1<<ADLAR) | (1<<MUX1);                         // 2019-01-28 ref-1V1 early turn-off
       PORTB &= ~OutPoti;                                      // 2019-01-28 save power
       PRR |= (1<<PRADC);                                      // 2019-01-28 save power
       if (WDTirqCount==0) ShutDown();                         // 2019-01-06
       //sleep_cpu();                                            // 2019-01-28!!!
   }
}
/*
changelog ******************************************************

2019-01-06b
GetVcc: UVLO shutdown removed
cpu-load minimized
2019-01-18  PLLclock -> timer1 enabled
2019-01-23 disabled: Timer0, GetVcc, sleep
sineoutput with 16bit phase offset = 256:
61/122/245/357/357Hz @clk div 16/8/4/2/1
PCK/4 works: 245*256*256*4=64MHz & PWMclk=62.7kHz with 62.5kHz measured
PCLK/2 & /1 results in PWMclkmax=125kHz

2019-01-24
fusing the attiny
AVR8-Burn-O-Mat is broken, requires JRE7, but JRE8 is installed
fusing LFuse to E1 with AVRDude immediately without data file works like this:
/usr/bin/avrdude -P usb -c usbtiny -p t25 -U lfuse:w:0xe1:m
Timer1-Clk=32MHz -> fpwm=125kHz
MCU-Clk=16MHz    -> firq=125kHz
SineFreqTable: 4-16-62-250-1k-4k-16kHz
added GetPoti(), GetFrequ()

2019-01-26
bugfix: ADC-conversion stayed in endless loop: PRR had turned off ADC power!
no sleep  current consumption Icc=11.2mA @3V6 &10k pots ¬ frequ <=1kHz
main idle current consumption Icc=10.2mA @3V6 &10k pots ¬ frequ <=1kHz
output-filter is 4.7mH 10nF + 1k0 12nF

2019-01-27
output filter is 4.7mH 10nF 2k7 4n7||1n0
Watchdog-timeout restored. Now WDT-irq is always running. To avoid timeout-restart on single ADC-spikes,
GetVcc(): hi Vcc voltage 1x increments timeout counter instead of reloading full timeout.
bugfix shutdown: timer1 was not stopped before PRR disable & thus continued working
10Min Watchdog timeout shutdown verified

2019-01-28
GetVcc(): Watchdog timeout countup limited to WDTirqCountDown
10Min Watchdog timeout shutdown verified
THD measurements updated
for instanteneous frequ-poti response: WDTirq speedup from 2ticks/1sec -> 1tick/64ms
Main loop exits sleep mode, wake-up 1/64ms WDTirq
save power: ADC turn-off saves about 0.1mA
save power: frequ-poti turned off outside main
bug: sleep main is terminated by timer1irq, but sleep_cpu on timer1Irq-exit freezes the system

Todo:
turn-off frequ-poti
GetVcc(): measure ref1V1 against Vcc - saves a portbit
250kHz PWM-clk
*/
...mit der Lizenz zum Löten!
 
Reply
#58
ok, ich sehe es ein. Um irgendeinen irq code aus zu führen, muß die MCU aufgeweckt werden. Ist irgendwie logisch.
Aber ich raffe es nicht weshalb ich den irq nicht mit sleep-idle beenden darf.
...mit der Lizenz zum Löten!
 
Reply
#59
(29.01.2019, 12:42 AM)voltwide schrieb: Aber ich raffe es nicht weshalb ich den irq nicht mit sleep-idle beenden darf.

Dann musst du du den PC per hand "verbiegen" sonst gibt es ein stack overflow.
Irq sichert die Rücksprungadresse und Reti schreibt popt die Adreesse in den PC zurück.
 
Reply
#60
(29.01.2019, 08:43 AM)Bandre schrieb:
(29.01.2019, 12:42 AM)voltwide schrieb: Aber ich raffe es nicht weshalb ich den irq nicht mit sleep-idle beenden darf.

Dann musst du du den PC per hand "verbiegen" sonst gibt es ein stack overflow.
Irq sichert die Rücksprungadresse und Reti schreibt popt die Adreesse in den PC zurück.
Die ganze Idee ist beerdigt, denn selbst wenn es mir gelänge dass die Timer1-irqs mit sleep enden -
würde die 1x pro WDT-irq aufgerufen main-Prozedur von mehreren zwischenzeitlich eintreffenden Timer-Irqs zerlegt werden.
...mit der Lizenz zum Löten!
 
Reply