Hoi iedereen,
Ik wilde deze vakantie eens een beetje stoeien met het programmeren van AVR's. Ik heb op school al eens gewerkt met AVR Studio (v4) om een project met een Pololu robot te maken. Aangezien ik voor school ook een Digilent JTag-usb kabel heb moeten aanschaffen, wil ik hiermee nu ook mijn eigen attiny's programmeren. Het programmeren lukt netjes, ik heb een voorbeeldprogramma van internet geplukt en de .hex met succes in de Attiny geladen.
De volgende stap was om een nieuw AVR studio project te maken en de code uit de .c van het voorbeeldproject hierin te zetten. Deze heb ik gecompileerd. Daarbij werd er netjes een .hex aangemaakt. Deze .hex heb ik met de digilent AVR programmer in de attiny geladen. Nu komt het gekke: de attiny doet niets.
Het gaat om deze code:
/* Copyright Mark Vitnell 2006 */
#include <avr/io.h>
void delay(long int ticks) {
unsigned long int countdown;
while (ticks != 0) {
for (countdown=0; countdown <= 200; countdown++); // about 1ms @9mhz
ticks--;
}
}
int main(void) {
DDRB= (1<<DDB3)| (1<<DDB4); // data direction to output for LED ports
while (1) { // forever
PORTB = 0x08; // turn on LED on PB3
delay(30); // for about 400ms
PORTB = 0x10; // turn on LED on PB4
delay(20); // for about 100ms
}
return 0;
}
Echter, als ik de code van deze link gebruik en compileer dan gaat de led wel netjes knipperen.
Heeft iemand enig idee hoe dit kan? Ik heb een gevoel dat de klok van de attiny niet geactiveerd is of iets dergelijks. Moet ik in de AVR programmer nog iets doen bij het tabblad fuses?
[Bericht gewijzigd door Henry S. op (0%)]
Die for loop in delay() heeft geen effect op de code (ticks wordt niet gebruikt), en zal dus weggeoptimaliseerd worden. Gebruik util/delay.h zoals in het voorbeeld, of als je die met alle geweld wil vermijden moet je eens op volatile zoeken.
Verder is afhankelijk van de kloksnelheid een loop van 4000-6000 iteraties waarschijnlijk veel te snel om waar te nemen. Zie de stopwatch optie in de simulator van AVR studio.
zoiets als _delay_ms(500) doet volgens mij precies wat je wil. Wel optimalisaties aanzetten en de kloksnelheid invullen bij project configuratie als je met util/delay.h werkt.
flipflop
"We cannot solve our problems with the same thinking we used when we created them" - Albert Einstein
Of ticks echt weggeoptimaliseerd wordt betwijfel ik. Maar het klopt dat het zou mogen.
Zie je de LEDs helemaal niet branden? Als je delay te snel zou zijn, dan moeten ze nog steeds wel 50% van de tijd aan zijn, en dus op halve kracht branden.
avr-gcc -Os maakt er dit van bij mij:
ldi r24,lo8(24)
out 55-32,r24
ldi r25,lo8(8)
ldi r24,lo8(16)
.L4:
out 56-32,r25
out 56-32,r24
rjmp .L4
Oftewel zet DDRB op de goede waarde, en schrijf dan zo snel mogelijk afwisselend 8 en 16 naar PORTB. Zal inderdaad de LED's op iets van 25%/75% laten branden (die rjmp zorgt voor 2 cycles extra delay).
rew
four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
In de handleiding van gcc staatstond dat lege loops maar 1 reden kunnen hebben en dat is timing. Dus vandaar dat er binnen gcc voor gekozen is om die NIET weg te optimaliseren.
[update] Mogelijk zijn ze die publieke interface-policy aan het herzien. Zucht.
[Bericht gewijzigd door rew op (22%)]
Ik had me niet gerealiseerd dat het knipperen zo snel ging. Ik heb het even bekeken met de scoop en het blijkt te kloppen. Echter, wat ik vreemd vind, is dat ik wel iets zie knipperen als ik de .hex gebruik die al bij het project zat.
Uiteindelijk is het de bedoeling dat ik een RGB-led kan aansturen zodat deze langzaam door de kleuren van de regenboog fade. Ik heb hiervoor onderstaande code bedacht:
#include <avr/io.h>
#define max_duty_cycle 1000
#define Time_high duty_cycle
void Red_fade_in(void);
void Red_fade_out(void);
void Green_fade_in(void);
void Green_fade_out(void);
void Blue_fade_in(void);
void Blue_fade_out(void);
void wait(unsigned long int);
unsigned long int duty_cycle = 0;
int main (void)
{
DDRB= (1<<DDB3)| (1<<DDB4)| (1<<DDB5); //set PB3, PB4, PB5 as output
while(1)
{
Red_fade_in();
Blue_fade_out();
Green_fade_in();
Red_fade_out();
Blue_fade_in();
Green_fade_out();
}
}
void wait(unsigned long int cycles)
{
int n=0;
for(n=0; n<=cycles; n++)
{
}
}
//blauw brandt , rood fade langzaam in
void Red_fade_in()
{
unsigned long int Time_low = max_duty_cycle - duty_cycle;
while (duty_cycle <= max_duty_cycle)
{
Time_low = max_duty_cycle - duty_cycle;
PORTB = 0x28; //blauw en rood aan
wait(Time_high);
PORTB = 0x20; //blauw aan
wait(Time_low);
duty_cycle = duty_cycle + 1;
}
}
//rood brandt , blauw fade langzaam uit
void Blue_fade_out()
{
unsigned long int Time_low = max_duty_cycle - duty_cycle;
while (duty_cycle >= 0)
{
Time_low = max_duty_cycle - duty_cycle;
PORTB = 0x28; //rood en blauw aan
wait(Time_high);
PORTB = 0x08; //rood aan
wait(Time_low);
duty_cycle = duty_cycle - 1;
}
}
//rood brandt , groen fade langzaam in
void Green_fade_in()
{
unsigned long int Time_low = max_duty_cycle - duty_cycle;
while (duty_cycle <= max_duty_cycle)
{
Time_low = max_duty_cycle - duty_cycle;
PORTB = 0x18; //rood en groen aan
wait(Time_high);
PORTB = 0x08; //rood aan
wait(Time_low);
duty_cycle = duty_cycle + 1;
}
}
//groen brandt , rood fade langzaam uit
void Red_fade_out()
{
unsigned long int Time_low = max_duty_cycle - duty_cycle;
while (duty_cycle >= 0)
{
Time_low = max_duty_cycle - duty_cycle;
PORTB = 0x18; //groen en rood aan
wait(Time_high);
PORTB = 0x10; //groen aan
wait(Time_low);
duty_cycle = duty_cycle - 1;
}
}
//groen brandt , blauw fade langzaam in
void Blue_fade_in()
{
unsigned long int Time_low = max_duty_cycle - duty_cycle;
while (duty_cycle <= max_duty_cycle)
{
Time_low = max_duty_cycle - duty_cycle;
PORTB = 0x30; //groen en blauw aan
wait(Time_high);
PORTB = 0x10; //groen aan
wait(Time_low);
duty_cycle = duty_cycle + 1;
}
}
//blauw brandt , groen fade langzaam uit
void Green_fade_out()
{
unsigned long int Time_low = max_duty_cycle - duty_cycle;
while (duty_cycle >= 0)
{
Time_low = max_duty_cycle - duty_cycle;
PORTB = 0x30; //blauw en groen aan
wait(Time_high);
PORTB = 0x20; //blauw aan
wait(Time_low);
duty_cycle = duty_cycle - 1;
}
}
Deze code heb ik eerst getest op mijn LPCXpresso-bordje. Hier werkte het perfect, maar op de attiny wil het niet werken.
[Bericht gewijzigd door Henry S. op (0%)]
buckfast_beekeeper
Van Lambiek wordt goede geuze gemaakt.
unsigned long intKlopt dit? Probeer eens uint32_t (max 4,294,967,295) of uint64_t (max 18,446,744,073,709,551,615)
void wait(unsigned long int cycles)
{
int n=0;
for(n=0; n<=cycles; n++)
{
}
}Gaat volgens mij zeer snel. Ik gebruik het onderstaande om korte pauzes in te lassen.
/***************************************************************
*
* wachtlus in ms
*
***************************************************************/
void delay_ms(uint16_t ms){
for(uint16_t t=0; t<=ms; t++){
_delay_ms(1);
}
}Gebruik de functies in util/delay.h of eventueel util/delay_basic.h, zoals is het voorbeeld van buckfast_beekeper. Let op dat je F_CPU moet definiëren (kan bij AVRStudio bij project opties) en je geen variabele argumenten aan de util/delay.h functies mag meegeven. _delay_ms(Time_low) werkt dus niet. Vandaar dat buckfast_beekeper hem in een loop stopt zodat hij variabele veelvouden van 1ms kan gebruiken. Aangezien dit geen lege loop is zal hij ook niet geoptimaliseerd worden.
Ik was inderdaad al tegen het probleem aan gelopen dat je de delay-functie niet met een variabele kunt aanspreken. Ik had er nog niet aan gedacht dat je delay_ms(1) in een loopje kunt zetten. Dat is een mooie, simpele oplossing.
Ik heb nu de volgende code werkend, om een led te laten in- en uitfaden.
#include <avr/io.h>
#include <util/delay.h>
#define period 1000
int time_low;
void wait (long int ticks)
{
if(ticks>0)
{
for (int teller = 0; teller<=ticks; teller++)
{
_delay_us(1);
}
}
}
void fade_out(void)
{
while(time_low<=period)
{
wait(time_low);
PORTB ^= 1<<PB4; //on
wait(period-time_low);
PORTB ^= 1<<PB4; //off
time_low++;
}
}
void fade_in(void)
{
while(time_low>=0)
{
wait(time_low);
PORTB ^= 1<<PB4; //on
wait(period-time_low);
PORTB ^= 1<<PB4; //off
time_low--;
}
}
int main(void)
{
// Set up Port B pin 4 mode to output
DDRB = (1<<DDB3)| (1<<DDB4);
// Set up Port B data to be all low
PORTB = 0;
time_low=0;
while (1)
{
fade_out();
fade_in();
}
return 0;
}
Deze code heb ik exact gekopieerd naar een nieuw project en er een attiny45 mee geprogrammeerd. Deze doet helemaal niets: de led flitst een fractie van een seconde bij het insteken van de voeding en voor de rest blijft hij uit. Hoe kan dit? Als de code op de attiny13 werkt, zou hij ook op een attiny45 moeten draaien toch? Ik heb bij de Attiny13 de F-cpu ingesteld op 9,6 MHz en bij de attiny45 op 8 MHz.
[Bericht gewijzigd door Henry S. op (0%)]
EricP
mét CE
Heb je ook verteld dat het voor een tiny45 is? Registers zouden wel eens ergens anders kunnen zitten...
Ja, ik heb een heel nieuw project aangemaakt (op dezelfde manier als ik dat voor de attiny13 doe) en aangegeven dat het om een attiny45 gaat.
Zelfs als ik simpelweg dit doe gebeurt er niets:
/* Copyright Mark Vitnell 2006 */
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
// Set up Port B pin 4 mode to output
DDRB = (1<<DDB3)| (1<<DDB4);
// Set up Port B data to be all low
PORTB = 0;
PORTB ^= 1<<PB4;
while(1)
{
PORTB ^= 1<<PB3;
}
return 0;
}
Overigens: de rede dat ik met de attiny13 te weinig geheugen heeft voor de manier waarop ik mijn rgb-fade-regeling maak.
Mij is nog onduidelijk hoe het werkt met F_CPU: als ik bij AVRstudio aan geef dat ik een bepaalde frequentie wil, zal de hardware in de attiny dan daadwerkelijk op deze snelheid gaan lopen? En wanneer moet je nu eigenlijk iets met de fuses van de microcontroller doen? En wat is in AVR Studio het verschil tussen de verschillende opties die ik kan kiezen bij optimalisatie?
[Bericht gewijzigd door Henry S. op (32%)]
Nee F_CPU, dat is voor de compiler. Hardware word geregeld door fusebits instellingen en oscillator(intern of extern) of kristal.
Je moet dus zowel F_CPU als de avr op dezelfde snelheid laten lopen.
Aha, dat zou wel eens het een en ander kunnen verklaren. Ik heb net geprobeerd om de fuses te programmeren, maar ik ben bang dat ik het verkeerd heb gedaan, want ik kan hem nu niet meer programmeren. Ik heb er echter wel op gelet dat Enable Serial programming aan stond. Is er een site die ik kan raadplegen over de fuses?
Ik kreeg al tijdens het programmeren van de fuses een foutmelding:
Creating SPI communications interface
Connecting
Entering programming mode
Failed in high speed mode, trying low speed mode.
Low speed mode succeeded
Checking device signature
Programming fuses
Verifying fuses
*** Verify Error - Fuse bits different than value written
Leaving programming mode
Ik heb de volgende fuse-settings gebruikt voor de attiny13:
[SELFPRGEN = 1] Self Programming Enable
[DWEN = 1] DebugWire Enable
[BODLEVEL1 = 1] Enable BOD and select level
[BODLEVEL0 = 1] Enable BOD and select level
[RSTDISBL = 0] Disable external reset
[SPIEN = 1] SPI Programming enable
[EESAVE = 1] Keep EEprom contents during chip erase
[WDTON = 1] Watch dog timer always on
[CKDIV8 = 0] Start up with system clock divided by 8
[SUT1 = 1] Select start-up time
[SUT0 = 0] Select start-up time
[CKSEL1 = 1] Select Clock Source
[CKSEL0 = 0] Select Clock Source
Waar is het mis gegaan?
buckfast_beekeeper
Van Lambiek wordt goede geuze gemaakt.
Een site met fuse calculator is deze.
SPIEN heb je op 1 gezet. Deze behoort normaal 0 te zijn. Waarschijnlijk doelt de foutmelding daar op met "Fuse bits different than value written". Via SPI kan je deze fuse normaal niet wijzigen. Ze zal nog altijd als 0 in de Attiny staan. Uit de datasheet
Notes: 1. The SPIEN fuse is not accessible in SPI Programming mode.
Een fout die zeer vaak gemaakt wordt bij het zetten van de fuses. 0 wil zeggen geprogrammeerd. 1 is dan uiteraard niet geprogrammeerd. Ook uit de datasheet:
Note that
the fuses are read as logical zero, "0&", if they are programmed.
De low byte zou standaard 01101010 moeten zijn jij hebt 11101010 gebruikt. Zoals reeds aangehaald hierboven wordt de 1 in bit 7 normaal genegeerd.
Dezelfde fout maak je bij "[RSTDISBL = 0] Disable external reset". Deze is standaard 1. Je hebt de reset uitgang uitgeschakeld. Zonder de reset kom je via SPI niet meer in programmeermode. Alleen een HVPP kan je nog redden.
Dus het had dit moeten zijn?
[SELFPRGEN = 1] Self Programming Enable
[DWEN = 1] DebugWire Enable
[BODLEVEL1 = 1] Enable BOD and select level
[BODLEVEL0 = 1] Enable BOD and select level
[RSTDISBL = 1] Disable external reset
[SPIEN = 0] SPI Programming enable
[EESAVE = 1] Keep EEprom contents during chip erase
[WDTON = 1] Watch dog timer always on
[CKDIV8 = 0] Start up with system clock divided by 8
[SUT1 = 1] Select start-up time
[SUT0 = 0] Select start-up time
[CKSEL1 = 1] Select Clock Source
[CKSEL0 = 0] Select Clock Source
Een beetje verwarrend hoe het werkt met de fusebits. Had ik het selecteren van de interne 9,6 MHz klok wel goed gedaan?
buckfast_beekeeper
Van Lambiek wordt goede geuze gemaakt.
Wil je effectief op 9MHz6 klokken? Zet dan "[CKDIV8 = 0] Start up with system clock divided by 8" op 1. Nu is de klok deling door 8 actief. Je haalt zo 1MHz2 als effectieve klok. Misschien is dit ook de reden dat hij geen high speed mode kon programmeren.
Persoonlijk zou ik BODLEVEL1 en BODLEVEL0 ook op 0 zetten. Je µC gaat dan een brown out detectie uitvoeren en bij een spanning van minder dan 4V3 gaat hij resetten.
BTW je hebt 2 x SUT1 in je lijstje staan. SUT1 = 1, SUT0 = 0.
Het zelfde voor CKSEL. Je hebt CKSEL0 = 0 en CKSEL1 = 1.
Ik heb het even aangepast, scherp opgemerkt dat er twee keer hetzelfde stond. Nu is het wachten tot ik nieuwe tiny's binnen krijg (ik bestel er maar meteen wat op voorraad). Ik snap nu wel hoe het werkt. Bedankt voor de hulp.
pros
Prosper, yop la boum, c'est le roi du macadam (aldus Maurice Chevalier)
Bij Atmel hebben ze dat goed gezien: geef de gebruiker de mogelijkheid om het RSTDISBL via ISP te activeren, zodat ISP meteen onbruikbaar wordt. De onwetende nieuwkomer trapt in de val, en besteld meteen een voorraadje nieuwe AVR's... 
Als het enkel je bedoeling was, op 9.6MHz te draaien, had je niet eens een fuse-bit hoeven te wijzigen. Dat kan ook via software:
int main(void)
{
CLKPR = (1 << CLKPCE);
CLKPR = 0; // Schakel CKDIV8 uit
// Enz...
Alleen een HVPP kan je nog redden.
Nou, eigenlijk niet. Je kan een ATtiny13 niet met HVPP programmeren, wegens het gebrek aan pinnen.
Met HVSP kan je wèl, maar dat bedoelde je waarschijnlijk? AVR-Doper is een vrij eenvoudig na te bouwen HVS-Programmer. Daarmee kan de TS zijn arme tiny reanimeren, of hij kan de RESET-pin als I/O-pin gebruiken. Soms komt een extra-pin handig van pas.
Op 10 augustus 2011 21:00:07 schreef pros:
Bij Atmel hebben ze dat goed gezien: geef de gebruiker de mogelijkheid om het RSTDISBL via ISP te activeren, zodat ISP meteen onbruikbaar wordt.
Hoe zou het anders moeten? De AVR's met RSTDISBL hebben nou eenmaal meestal geen JTAG. Geloof trouwens dat je via JTAG zowel SPIEN als JTAGEN kan uitzetten, dat is ook een leuke grap.
Daarmee kan de TS zijn arme tiny reanimeren, of hij kan de RESET-pin als I/O-pin gebruiken. Soms komt een extra-pin handig van pas.
Aan de andere kant kun je voor het geld dat het kost om het te maken wel weer aardig wat 'tiny13's kopen. Gaat natuurlijk niet op als je het als project doet.
Ik ben geen voorstander van RSTDISBL gebruiken, ik pruts er liever een grotere AVR in een kleine behuizing in. Als ik echt wanhopig ben zorg ik dat ik alle ontwikkeling op een grotere broer doe, zodat ik die HVSP procedure zo min mogelijk hoef te doen. Uit het socket trekken en in een programmer stoppen gaat erg snel vervelen. Ook is een ATtiny13 in DIP (wat min of meer verplicht is voor HVSP) niet veel kleiner dan bv. een ATmega48 in TQFP.