Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Development tool and IDE for ATmega328 in Assembly coding

Status
Not open for further replies.

Alan8947

Full Member level 4
Full Member level 4
Joined
Sep 21, 2016
Messages
215
Helped
9
Reputation
18
Reaction score
11
Trophy points
1,298
Activity points
3,746
Hi

I have been studying the datasheet of ATmega328, I finish 200pages including SPI, USART. I plan to finish study to about 270 that include 2-wires and ADC. I read the assembly codes in the examples in the datasheet, so far, I understand them. It was hard at the very beginning to get used to the datasheet, but it's been smooth.

I want to try writing some assembly code to play around. I bought a kit from Amazon a year ago planning to play with my grand daughter, but as I study, I really got interested and study more than I should. I don't know whether this kit can even do assembly coding. Anyone familiar with this kit?

https://www.amazon.com/dp/B08C4SK6H3

If not, can anyone suggest what to buy and what IDE I should use to do assembly. Any good books? I have a few books, but I think they are all in C++. I'll get to C++ eventually. But for now, I want to get my hands wet on assembly.

I am an old timer, my career started with assembly programming in 1980 with 8085. I still like assembly programming. Call me crazy. :p :D

thanks
 

Hi,

as far as I can see the kit does not come with software, like IDE, compiler, assembler.

In so far the "the kit" can´t work with assembler.

But usually every microcontroller manufacturer provides a suitable assembler for their chips. But there are also second source assembler available.(I guess there is a GNU assembler for AVRs ..)

The input file for an assembler usually is a plain ASCII text file. Every simple editor can do this. But if you want some assitance for writing code (like syntax highlighting ..) you may use an IDE. There are many.
Maybe the most common for AVRs is the AVR Studio. With it you can write assembler code as well as C code .... and is optimized for AVRs.

Eclipse is a rather advanced IDE, UltraEdit is less complex .. and there are many others.

*****
Assembler indeed names two different things:
* The assembler code, the language, the source file
* and the engine (PC software) that translates the assembler source code into machine code (usually a HEX file).

*****
For more detailed AVR stuff you may look at dedicated AVR forums. Like AvrFreaks.

Klaus
 
ok, assembly for AVR? there are two options:
AVRASM2 (wihch comes with AVRStudio IDE)
or AVR-GAS (or just AS the GNU assembler for AVR)

for the atmega328?
I would go for GAS but, it's not easy to install.
you can work with a couple of IDEs like WinAVR it comes with GNU Make, GCC if needed, and a linker, sadly the last distribution is from 2010 but it should work with your 328 or 328p (if you have a newer chip like the 328ap you can do some magic to make it compatible)

another way is to install the Arduino IDE and use its GCC distribution to use the assembler, but for that I think it's better to combine with another tool, like VSCode. here's a tutorial for that https://www.instructables.com/Programming-AVR-Chips-Using-Visual-Studio-Code/
in this case the GCC is newer than the one that comes with winAVR, and you can make it up to date just updating the arduino ide.

just note that most documentation from MicroChip regarding the assembler is related to AVRASM2, so please take a look to the avr-libc regarding the differences with GAS
 

    Alan8947

    Points: 2
    Helpful Answer Positive Rating
Thanks, I definitely look into that.

The other question is: If I cannot use the setup I got from the kit mentioned above, I need to buy new hardware from scratch, any suggestion what to buy? Like how to hook up to the computer, how to put the ATmega328 DIP chip in for programming and play with it.

I am literally starting from scratch, like I said my experience was 8085 and I pretty much an analog RF engineer since the 80s.

I am not saying I want to be an expert, just looks fun reading the assembly code in the data sheet, so I am curious just to try it. I will go back to C++ type and use the kit in the future.

BTW, The datasheet show both assembly code AND C++ code that do the same thing. I don't see the C++ is any shorter or simpler. Why use C++? Is it just because people don't want to learn Assembly coding as it's harder? They just want to use something they know? Is C++ SLOWER than Assembly?( From my understanding, C++ got to be slower and use a lot more memory. I remember we could do a lot with 64K total memory in 8085).

thanks
 

I think your kit is good enough to work, I don't feel you need to buy anything extra.
just take in consideration that:
the atmega328 in the arduino board, is already programmed with a bootloader. that means that you don't need a fancy programmer, and you can use its serial port (which it goes through another fancy usb-serial converter) so you can program it directly from the PC.

you can always use a ISP programmer to update the firmware via its ISP pins (mostly its SPI port) or a HVPP (High Voltage Parallel Programmer) which uses most pins to program... (useful if you messed the ISP enable fuse) but to be honest, it is 100% better to learn an use the serial bootloader.

regading C (the manual uses C most of the time, just note that arduino is something in the middle of C++ and plain C (and that's because the memory allocation of C++ is very hungry and thus not completely implemented in AVR-GCC)) this processor was created with High level programming in mind. the AVR-GCC compiler is very efficient. and the register usage make most of the hardware interface, just a registry configuration task, instead of plain bit-bang implementation.

can you make Assembler code faster that C code? indeed, the avr-libc have a very fixed approach regarding general purpose register usage (like reg1 is always 0, why? 'cuz the value 0h is always needed and useful to many operations), memory usage (heap vs stack) function calling and return.... (some info here),
but how faster is your assembler code in comparison? after the overhead you can achieve over 20% to 30% 'faster' code (initialization and function calling. but.... (and just take in consideration that this is a common opinion, but just opinion) compared with other microcontrollers of similar characteristic, for example a MCS-51 this AVR processor is at least 12 times faster, (or a PIC16, the AVR is at least 4 times faster) thats about 1200% or 400% faster, maybe we can tolerate that 30% of 'slowness' if we don't break our minds with register handling, the instruction set, stack managing, etc. and use a 'high level' programming like is C. (this also applies to memory usage, both for code and data, a MCS51 with it's internal 256 bytes can't be compared with the 2048 bytes of the m328)

that's something you'll see discussed with bigger processors, like an STM32 or ESP32, very few people works in assembler in those processors, it's better to focus on solve the problem at hand with a higher level language at the cost of use more resources... here people don't use 'low level' C, but instead something higher like LuA or Python...

any way, the AVR has a very cute instruction set, it's very efficient and not-too-difficult to learn (compared to let's say ARM or ARMThumb) if you feel comfortable to use assembler just do it, but once you get to more a more complex code, you'll see that a higher level language like C can make the code more readable and thus, more maintainable.


just for example, I loved to program in Assembler, I have made a library for almost any basic peripheral I found (like keypads, LCDs, almost any I2C and SPI peripheral, even USB with a PIC18) but I really succumbed to Ethernet communication. it was very complex, and I had to fall back to an library and just use it and focus on the usage, data packets and so... (sorry for the last rant)
 
Thanks Kurenai ryu

I am still debating, just getting more info. I want to read the datasheet first, but I might just go directly to writing C++ if it's so much trouble. I read about 210 pages, just have to read to about 280 and I am done. then I'll make up my mind. I do want to play with my grand daughter soon, so it's not a bad idea to just skip the assembly coding. That will take a while to do.

Thanks

 

Hi,

I read about 220pages of datasheet. Although there's a lot of setup for different things like SPI, USART, INT, WD etal., But when I read the DEFAULT settings of all the I/O registers, it put EVERYTHING in disable, leaving just the basic digital POET B, C D as digital port. Just program the DDRn and I am done.

If I just start with simple driving LED with port blinking, it's quite simple even if I program in assembly from scratch and not using any outside programs. That's all I want to try out.

Just blink LED, read a switch to do light up another LED, maybe using interrupt (INT0) to sense a switch and light up LED etc. I should just do it with a few lines code in Assembly.

Tell me if this is correct. Thanks
 

Show me your code then I can say whether it´s correct.

From your textual description I see nothing wrong.

Klaus
I am not close to that point. I just read the datasheet and look at the initial value of all the registers, they are all disable. Even the Stack Pointer default to the bottom of the SRAM and the Interrupt Vector default to the top of the program memory. It's all set up to just play with it.

Now I have to know how to down load and put program into the Flash or EEPROM.

I am more talking at this point. I really want to read to about 280 pages. Not just for assembly programming, I never learn SPI, USART and all that. This is an opportunity to learn what they are for once. All the parity error, start, stop bit and all that. Like I said before, I am an Analog RF guy, haven't tough this stuff since 8085. Even when I designed with HC11 it's a lot simpler and I just design the circuit and had my jr engineer do the programming. I am so so far behind time it's not funny.

I am not wanting to do assembly because I think it's useful. It's more for OLD TIME SAKE. I know I have to go back to C++ soon as the main goal of doing this is to play with my grand kids. Grandson graduate in CS major BUT he never learn firmware. I want to give my grand daughter a taste of firmware as she is going to college next year. Firmware is a HUGE field and I want them to experience with it by playing with the kit I bought.

Thanks
 
Last edited:

just because you didn't mention, also check (maybe print and always have at hand) the schematic of the board, it seems a R3 like this one https://www.arduino.cc/en/uploads/Main/Arduino_Uno_Rev3-schematic.pdf
(ignore the mega16u2 right now, it works only as a USB to serial converter and focus on the mega328 and the external connectors).

so for starters we already have a proper LED in the board at Pin13 (PB5/SCK) I would start with that led!

I would start with something like this:
Code:
;hello.S
;  turns on an LED which is connected to PB5 (digital out 13)

; manual definitions -- later it can include a header file
#define PORTB 0x05
#define DDRB 0x04
#define PB5 5
#define RAMEND 0x8FF
#define SPL 0x3D
#define SPH 0x3E


; local definitions
#define LED_PIN (1<<PB5)

#define DELAY_REGISTER R23


.global main
main:
  ; initialize stack -- recomended!
  ;ldi    r16,lo8(RAMEND)        ; load low byte of RAMEND into r16
  ;out    SPL,r16            ; store r16 in stack pointer low
  ;ldi    r16,hi8(RAMEND)    ; load high byte of RAMEND into r16
  ;out    SPH,r16   
; setup
  ldi R16,LED_PIN
  out DDRB,R16
; main loop
Start:
  clr R16
  out PORTB,R16
  ldi DELAY_REGISTER, 50
  rcall delay10ms
  ldi R16,LED_PIN
  out PORTB,R16
  ldi DELAY_REGISTER, 50
  rcall delay10ms

  rjmp Start


#define DELAY_VALUE 39998
delay10ms:
  ldi    R24,lo8(DELAY_VALUE)    ; intialize inner loop count in inner
  ldi    R25,hi8(DELAY_VALUE)    ; loop high and low registers

iLoop:    sbiw    R24,1        ; decrement inner loop registers (as word:16bits)
  brne    iLoop            ; branch to iLoop if iLoop registers != 0

  dec    DELAY_REGISTER            ; decrement outer loop register
  brne    delay10ms
  ret


later you can assemble with gcc
Makefile:
avr-gcc -x assembler-with-cpp -Os -Wall hello.S -o hello.o
avr-gcc hello.o -nostartfiles -o hello.elf
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock hello.elf hello.hex

it will create a hex file similar to this:
Code:
:1000000000E204B9002705B972E305D000E205B9A2
:1000100072E301D0F7CF8EE39CE90197F1F77A956F
:04002000D1F7089577
:00000001FF

later you can program the hex file with avrdude in the arduino board:

Makefile:
avrdude -Cavrdude.conf -v -patmega328p -carduino -PCOM13 -b9600 -D -Uflash:w:hello.hex:i



to be honest, for assembler you can try with AVRASM2 with AVRStudio (I think now it's called MPLAB for AVR) or other tools, also, the avr-gcc that comes with WinAVR (and Arduino) also comes with Make.exe and winAVR has a tool to create makefiles easily (for both C and Assembler Files) it reduces greatly the hassle of compiling/Assembling and even programming (by calling avrdude from itself)


just for completion, you should consider having the linking part of gcc, in this small example you should assemble with:

Makefile:
avr-gcc -mmcu=atmega328 -x assembler-with-cpp -Os -Wall hello.S -o hello.o
avr-gcc -mmcu=atmega328 hello.o -nostartfiles -o hello.elf
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock hello.elf hello.hex

it will generate a bigger hex
Code:
:100000000C9434000C943E000C943E000C943E0082
:100010000C943E000C943E000C943E000C943E0068
:100020000C943E000C943E000C943E000C943E0058
:100030000C943E000C943E000C943E000C943E0048
:100040000C943E000C943E000C943E000C943E0038
:100050000C943E000C943E000C943E000C943E0028
:100060000C943E000C943E0011241FBECFEFD8E04C
:10007000DEBFCDBF0E9440000C9452000C940000E3
:1000800000E204B9002705B972E305D000E205B922
:1000900072E301D0F7CF8EE39CE90197F1F77A95EF
:0800A000D1F70895F894FFCF99
:00000001FF

which just fills the interrupt vector and makes some initialization before calling the code.


and just for comparison: in C this would be like:

C:
#include <avr/io.h>
#include <util/delay.h>
#define LED_PIN (1<<PB5)
int main(void) {
  DDRB = LED_PIN;
  while(1) {
    PORTB = 0;
    _delay_ms(500);
    PORTB = LED_PIN;
    _delay_ms(500);
  }
}

a quick compile with:
Makefile:
 avr-gcc -mmcu=atmega328 -DF_CPU=16000000 -Os -Wall hello.c -o hello.o
 avr-gcc -mmcu=atmega328 -DF_CPU=16000000 hello.o -nostartfiles -o hello.elf
 avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock hello.elf hello.hex

generates an hex file like:
Code:
:100000000C9434000C943E000C943E000C943E0082
:100010000C943E000C943E000C943E000C943E0068
:100020000C943E000C943E000C943E000C943E0058
:100030000C943E000C943E000C943E000C943E0048
:100040000C943E000C943E000C943E000C943E0038
:100050000C943E000C943E000C943E000C943E0028
:100060000C943E000C943E0011241FBECFEFD8E04C
:10007000DEBFCDBF0E9440000C9457000C940000DE
:1000800080E284B915B82FEF39E698E1215030406D
:100090009040E1F700C0000085B92FEF39E698E104
:1000A000215030409040E1F700C00000EBCFF894C1
:0200B000FFCF80
:00000001FF


disclaimer: the delay code for the ASM I got from an old example, which probably uses another crystal, so the exact time could be different -- too lazy to recalculate right now.

Another disclaimer, I couldn't find an unused arduino to test in bare-metal, I know the avrdude command is correct (maybe you need the complete path for the conf file), but I just simulated the LED in simulide.
 
Wow, thanks Kurenai ryu

I am not that good, I need to copy your stuff down and read and study.

Thanks
 

Hi,

I just want to repeat what I and others already told:

No one wants you to read and understand the whole datasheet. I could not do this. I´m the person for "learning by doing". It would take me 10 times longer to learn by reading. An overview is important just to have a clue about the features. But the detailes I learn when I need them.
For sure you are free to go your own. way. Every person is different.

Btw: good job of Kurenai_ryu


Klaus
 
Hi,

I just want to repeat what I and others already told:

No one wants you to read and understand the whole datasheet. I could not do this. I´m the person for "learning by doing". It would take me 10 times longer to learn by reading. An overview is important just to have a clue about the features. But the detailes I learn when I need them.
For sure you are free to go your own. way. Every person is different.

Btw: good job of Kurenai_ryu


Klaus
Thanks for the advice. I am almost done, 3/4 way through.

I am just curious about the hardware as I am a hardware engineer, not a programmer even I started as one for short time. I am curious on the format of SPI, USART data, so it's not a total waste for me.

Honestly, I have not commit to do assembly even though I want to, still kind of debating. I hope I can finish reading in like a week or two.

Part of me tell me I should just play with the kit I have and see, part of me that is curious to go to assembly. Still fighting within me!! :p
 
Last edited:

Part of me tell me I should just play with the kit I have and see, part of me that is curious to go to assembly. Still fighting within me!! :p
Do it the way that is the best for you:
If you are good in learning from reading papers .. do so.
If you are good in learning form experiments .. do so.

My brother is more the "reading type", I´m more the practical type.
There is not one generally better than the other.

Klaus
 

Do it the way that is the best for you:
If you are good in learning from reading papers .. do so.
If you are good in learning form experiments .. do so.

My brother is more the "reading type", I´m more the practical type.
There is not one generally better than the other.

Klaus
I am the kind that read and design by theory before commit into it. Hardware is not as easy to experiment. I studied RF, doing all the Smith Chart and simulation before I put in circuit. I verified with network analyzer to draw the smith chart and matched very close to the real circuit. Most of my designs were one time through. I spend a lot of time study, analyze before commit. That's my way of doing things.

Even in the early days designing hardware, MPU, 8085, I drew out all the timing diagram putting in variation of prop delay of components to verify before putting onto pcb. Always worked one time through and proved to be reliable.

But in this case, I really have not decide whether I want to stop and do the assembly coding. I know I shouldn't, just I am an old timer came from assembly programming. But I do want to play with my grand kids on this and assembly just delay that.

thanks
 

Actually reading the datasheet is not hard. The bigger road block for me was the 3 separate address and data bus for I/O SRAM(I/O regs), flash program, and EEPROM. That's where I got stuck the most. Since then, it's quite smooth.

There are some patterns how things are done. Like all Interrupts are the same, have to have "I" in SREG set, each individual Interrupt ENABLE in their respective control reg., then the MASK is off and enable Interrupt flag. After knowing that, things are getting a little predictable.

I still have 2 Wires, ADC and boot loader part, that's it. It's interesting to know as I am a hardware guy at heart. I might skip the 2Wires and ADC. Absolutely have no intention to use those. Too bad it doesn't have DAC.
 

There is another quick and easy way to program an Arduino in assembly language, that does not require any additional software. That is to use in-line assembly language in the Arduino IDE, in a normal sketch.

Here is a quick blink example sketch that has the C functions commented out followed by inline assembly
code to accomplish the same function. It compiles and uploads to the Arduino in the IDE just as a normal sketch would. This example shows how to use the "asm" directive to insert assembly code into the sketch.

Code:
#define ledPin 13
// blinks the LED on Arduino UNO pin 13 using a timer1 interrupt

void setup()
{
  // pinMode(ledPin, OUTPUT);  // C - set LED pin 13 as output
  asm("ldi r16,0x20 \n"       // DDRB address 0x04 = 0x20 PINB5 output
    "out 0x04,r16 \n");
  
  // initialize timer1 -------------------------------------------------

  // noInterrupts();          // C - disable all interrupts
  asm ("cli \n");             // disable all interrupts
 
  // TCCR1A = 0;              // C -  clear TCCR1A anf TCCR1B
  // TCCR1B = 0;
  asm("ldi r16,0x00 \n "      // C - clear TCCR1A anf TCCR1B
   "sts 0x80,r16 \n"          // TCCR1A = 0x80
   "sts 0x81,r16 \n");        // TCCR1B = 0x81
  
  // TCNT1 = 34286;           // C - preload timer 65536-16MHz/256/2Hz
  asm("ldi r17,0x85 \n"       // preload timer 65536-16MHz/256/2Hz- Set TCNT1 to 34286 = 0x85EE
    "ldi r16,0xEE \n"
    "sts 0x85,r17 \n"         // TCNT1H = 0x85 
    "sts 0x84,r16 \n" );      // TCNT1L = 0c84     

  // TCCR1B |= (1 << CS12);   // C  - 256 prescaler
  asm("ldi r16,004 \n"        // 256 prescaler
    "sts 0x81,r16 \n" );      // TCCR1B = 0x81

    
  // TIMSK1 |= (1 << TOIE1);  // C - enable timer overflow interrupt                                                       
  asm("ldi r16,0x01 \n"       // enable timer overflow interrupt 
    "sts 0x6F,r16 \n" );      // TIMSK1 = 0x6F   
    
  // interrupts();            // C enable all interrupts     
  asm("sei \n");              // enable interrupts                     
}

ISR(TIMER1_OVF_vect)          // interrupt service routine for Timer 1 overflow
{
  // TCNT1 = 34286;           // C preload timer
  asm("ldi r17,0x85 \n"       // preload timer 65536-16MHz/256/2Hz- Set TCNT1 to 34286 = 0x85EE
    "ldi r16,0xEE \n"
    "sts 0x85,r17 \n"         // TCNT1H = 0x85
    "sts 0x84,r16 \n");       // TCNT1L = 0x84   
 
  // PORTB = (PINB ^ 0x20);   // C toggle LED - PORTB = xor (^) of PINB and b00100000- toggle LED   
  asm("in  r24, 0x03 \n"      // PINB (0x03) to r24 
    "ldi r25, 0x20 \n"        // b00100000 to r25
    "eor r25, r24 \n"         // exclusive or r25 and r24
    "out 0x05, r25 \n");      // result in r25 to PORTB (0x05)       
}


void loop()
{
  // your program here...
}
 
Hi Kurenai ryu

I am starting to read you reply in post #10. I have some very basic question on the program. I wrote my question in RED. This is the first time I read a real assemble code, not just a clip of example. I want to learn how to define the variable, register and all that. So it might be really stupid question.
Question to Kurenai_ryu.jpg


Thank, I still have to read the rest of it.

Can you give me a link how to write a complete program with #include, how to define things and all that?

Thanks
 

Hi,

Please don´t post text as a picture. We can´t copy and paste text from the picture to refer to.
You may use the icons in the editor window to upload/insert text as code or as comment
***
this is text in font "Courier New"
including white spaces

***

this is a quote .. using the QUOTE button

Code:
this is code using the CODE button

******
To your questions:
You can rename R23 with a different name?
Yes. Basically "R23" is a variable (name) with the content "23"
The same is true for "PORTB", which just contains the value of the PortB address.
So the line "'define DELAY_REGISTER R23" just generates a new variable with the value 23.

This is why I multiple times wrote that you don´t use raw addresses in assembler.

In the above code there is this line:
< "sts 0x85,r17 \n" // TCNT1H = 0x85 >
.. my recommendation is to use
< "sts TCNT1H,r17 \n" //
.. for this there needs to be somewhere the definition: <#define TCNT1H 0x85;>
Depending on the assembler you use .. it may be already defined for you.

On remark on giving a register a different name:
It´s on your own risk to do this. Because then:
* LDI DELAY_Register, 0x01
* LDI R23, 0x12
both access the same register. So the second line overwrites the R23 register.
So you may decide a group of register with special names.
In my assembler programs I defined
* R2 with name "ZERO" and value 0x00, kept zero all the runtime
* R16 with name "TMP0" as general purpose register
* R17 with name "TMP1" as general purpose register


So using assembler you usally have a fiile with all the basic definitions for yout microcontroller.
* Some assembler software comes with such a definition file, mabe named "m328def.inc".
* You may download it,
* or you may write it on your own.
To make use of the defintions you usually need to write "#include "m328def.inc" at the top of your source file.
These "*.inc" files may contain definitions or any valid code, or even macros.
Your own "*.inc" files should be located at the same path as your source file.

Klaus
 
Hi Klaus

Thanks for your time.


My computer crapped out today ( SSD failed), I have to use my wife's computer waiting for the new one to arrive. Then download all the stuffs. I really don't have time to learn right now.

I really starting to have a change of heard reading the second part of Kurenai ryu's post #10. I have NO IDEA what are the Hex stuff and all. There's a lot of syntax and all that I have to learn to do the assembly coding, it will take a lot of time. It might be a stupid idea to spend all the time just for curiosity.

Grandson is coming to stay with us in one week, the most logical thing is for me to just drop the assembly coding and get on with the Elegoo kit I have and see what's happening. If I still have to urge for assembly, I just pick it up later.

I know C++ is the way of today, it should be easier for me too.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top