TP of the Week fabacademy is to become familiar with the programming of microcontrollers and processors. I'll start by programming the circuit that I realized few weeks ago: the helloWorld board.
First I check my board by flashing it with the example, following the protocol of the tutorial.
make -f hello.ftdi.44.echo.c.make avr-objcopy -O ihex hello.ftdi.44.echo.out hello.ftdi.44.echo.c.hex;\ avr-size --mcu=attiny44 --format=avr hello.ftdi.44.echo.out AVR Memory Usage ---------------- Device: attiny44 Program: 776 bytes (18.9% Full) (.text + .data + .bootloader) Data: 64 bytes (25.0% Full) (.data + .bss + .noinit) cedric@cedric-Inspiron-5520 ~/fabacademy/electronicDesign/programming $ make -f hello.ftdi.44.echo.c.make program-avrisp2-fuses avr-objcopy -O ihex hello.ftdi.44.echo.out hello.ftdi.44.echo.c.hex;\ avr-size --mcu=attiny44 --format=avr hello.ftdi.44.echo.out AVR Memory Usage ---------------- Device: attiny44 Program: 776 bytes (18.9% Full) (.text + .data + .bootloader) Data: 64 bytes (25.0% Full) (.data + .bss + .noinit) avrdude -p t44 -P usb -c avrisp2 -U lfuse:w:0x5E:m avrdude: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.00s avrdude: Device signature = 0x1e9207 avrdude: reading input file "0x5E" avrdude: writing lfuse (1 bytes): Writing | ################################################## | 100% 0.01s avrdude: 1 bytes of lfuse written avrdude: verifying lfuse memory against 0x5E: avrdude: load data lfuse data from input file 0x5E: avrdude: input file 0x5E contains 1 bytes avrdude: reading on-chip lfuse data: Reading | ################################################## | 100% 0.00s avrdude: verifying ... avrdude: 1 bytes of lfuse verified avrdude: safemode: Fuses OK avrdude done. Thank you. $ make -f hello.ftdi.44.echo.c.make program-avrisp2 avr-objcopy -O ihex hello.ftdi.44.echo.out hello.ftdi.44.echo.c.hex;\ avr-size --mcu=attiny44 --format=avr hello.ftdi.44.echo.out AVR Memory Usage ---------------- Device: attiny44 Program: 776 bytes (18.9% Full) (.text + .data + .bootloader) Data: 64 bytes (25.0% Full) (.data + .bss + .noinit) avrdude -p t44 -P usb -c avrisp2 -U flash:w:hello.ftdi.44.echo.c.hex avrdude: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.00s avrdude: Device signature = 0x1e9207 avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed To disable this feature, specify the -D option. avrdude: erasing chip avrdude: reading input file "hello.ftdi.44.echo.c.hex" avrdude: input file hello.ftdi.44.echo.c.hex auto detected as Intel Hex avrdude: writing flash (776 bytes): Writing | ################################################## | 100% 0.27s avrdude: 776 bytes of flash written avrdude: verifying flash memory against hello.ftdi.44.echo.c.hex: avrdude: load data flash data from input file hello.ftdi.44.echo.c.hex: avrdude: input file hello.ftdi.44.echo.c.hex auto detected as Intel Hex avrdude: input file hello.ftdi.44.echo.c.hex contains 776 bytes avrdude: reading on-chip flash data: Reading | ################################################## | 100% 0.23s avrdude: verifying ... avrdude: 776 bytes of flash verified avrdude: safemode: Fuses OK avrdude done. Thank you.
Avrdude say "thank you" : it's a good sign, but unfortunaly, the serial connection don't responds.
exploring different codes I found an Neil's Comment "lfuse set to 0x7E for 20 MHz xtal"
but in the makefile that I used, the fuse is Set to 0x5E
I try to understand the datasheet to solve this problem ...
In the datasheet, it is question of the calibration of the oscillator :
"... OSCCAL = 0x7F Gives a higher frequency than OSCCAL = 0x80 ..."
I have not been able to decode the exact code to understand what is this setting
but I see it comes to select the external clock and multiple clock.
In any case, the circuit works I can start trying to program
simply edit the Makefile by replacing:
program-avrisp2-fuses: $(PROJECT).hex avrdude -p t44 -P usb -c avrisp2 -U lfuse:w:0x5E:m
by :
program-avrisp2-fuses: $(PROJECT).hex avrdude -p t44 -P usb -c avrisp2 -U lfuse:w:0x7F:m
I chose easy: instead of writing in hexadecimal, I will use assembler ...
Based on the example of Neil, I'll try to do my LED flashing.
some ressources : french article
AVR Assembler User Guide
we need download the compiler gavrasm
Once unzipped the folder (or installed) you can compile a program with the command:
./gavrasm <fichier_source_asm>
for example in my case :
./gavrasm hello.ftdi.44.blink.asm
I can compile my code, but the LED does not blink ...
I try a delay loop as this :
delai: ;ldi temp, 255; max ;clr temp delay_loop:----------------------------------------------- dec temp | clr temp1 | delay_loop2:-------------------------------- | dec temp1 | | clr temp2 | |2x( 255 X 3 X 255 X 255) delay_loop3:------------------ |3X255X255 | dec temp2 |1X255 | | brne delay_loop3------------ | | brne delay_loop2-------------------------- | brne delay_loop--------------------------------------- ret
according my calculus : the loop takes 3 X 2 X 255^2 = 99488250 cycles
because the processor pulses at 20Mhz, the duration of the delay could be ~ 4,97 sec.
It is not : the LED don't blink visualy, so I think I do mistakes in my calculs...
but when I change the clock divider to 256 (according the datasheet)
ldi temp, (1 << CLKPCE) ldi temp1, (1 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);/256 out CLKPR, temp out CLKPR, temp1
flashing is apparent and about the expected duration. So I made calculation errors?
the LED now flashing, but I've lost my tiny !
If I try to upload another program I get :
make -f hello.ftdi.44.blink.asm.make program-avrisp2 avrdude -p t44 -P usb -c avrisp2 -U flash:w:hello.ftdi.44.blink.hex avrdude: stk500v2_command(): command failed avrdude: stk500v2_program_enable(): bad AVRISPmkII connection status: Unknown status 0x00 avrdude: initialization failed, rc=-1 Double check connections and try again, or use -F to override this check. avrdude done. Thank you. make: *** [program-avrisp2] Erreur 1
On the advice of Neil, I try to use the "-i" avrdude, which specifies the time in microseconds between each bit change.
But this change nothing...
I finally found a solution here : use the-B option to delete the program in 1024 (this is the option Bitclock period)
phew!
I will NEVER change the clock divider more !
I gave the clock to its original timing.
But heck! as it is difficult to wait the clock which rotates dizzying pace of 20 million cycles per second!
I finally found a more or less elegant solution, using two coupled records (in the form of words) that instead of cap 255, reaching 65535: it's been more cycles.
delai: mov r16,temp outer_loop:; duration ~ ldi r26, 0; set r26 to zero ldi r27, 0; set r27 to zero delay_loop:;~262143*65535+3= 17179541508 cycles adiw r26, 1; add 1 to r26 and r27 ldi r28, 0; set r28 to zero ldi r29, 0; set r29 to zero delay_loop1:;~4*65535+3= 262143 cycles adiw r28, 1; add 1 to r28 and r29; 2 cycles brne delay_loop1; if no overflow loop; 1 or 2 cycles brne delay_loop; if no overflow loop dec r16; dcrement R16 brne outer_loop; if no overflow loop ret
Experimentally, I found this loop runs around in a centiseconde.
Ie if you set the registry "temp" 100 the last time for a second:
ldi temp,100;delay 1 sec rcall delai; delay
and it works!!
here my program : hello.ftdi.44.blink.asm.zip
To make a little easier, I'll try to program the button in C.
pins:
PA7: button (to set pullup)
PB2: LED
some useful resources:
to understand the inputs and outputs http://avrbasiccode.wikispaces.com/
My program will adjust the flash rate my led with the button.
To do this, I need to make a variable delay.
When you press the button, a counter which serve for the period of flashing LED is incremented:
... while (1) { if (PINA & button_pin_in){//if button not pushed PORTB |= led_pin_out; // Turn LED on long_delay_ms(blink_delay); PORTB &= ~led_pin_out; // Turn LED off long_delay_ms(blink_delay); }else{//if button pushed blink_delay=10; while(!(PINA & button_pin_in)){ blink_delay+=10; _delay_ms(10); } } } ...
The basic function "_delay_ms" is certainly more convenient to use than in assembler, but it takes only constants, not variables.
to manage a variable time I was inspired by this page
creating a long_delay function:
void long_delay_ms(uint16_t ms) { for(ms /= 10; ms>0; ms--) _delay_ms(10); }
File: Hello.blink.button.44.zip
My program works pretty well, but it could works better, I should attach an interrupt to the button, because when the LED is blinking in its phase, the button is inactive.
but I lack time to implement it this week.
To be continued ...
As recreation, I try highlowtech hardware library to program directly from the tiny Arduino interface.
The purpose of the program is to adjust the luminance of the LED by pressing more or less time the button:
/* set the value of the led by pushing the button more or less longtime This example code is in the public domain. led : 8 button : 7 */ #define led 8 #define bton 7 int value; void setup() { value=0; pinMode(led, OUTPUT); pinMode(bton, INPUT_PULLUP); } void loop() { if (digitalRead(bton)==LOW){ if (value!=0){ value=0; } else{ while(digitalRead(bton)==LOW){ if (value<255) { value++; analogWrite(led,value); delay(40); } } } } analogWrite(led,value); delay(40); }
I found that the delay does not fully correspond with what is encoded as if the clock was multiplied: my period lasts about 200ms instead of 40ms in the code.