Pokazywanie postów oznaczonych etykietą ARM. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą ARM. Pokaż wszystkie posty

czwartek, 31 maja 2012

ARM (STM32) - Blink Led and Interrupt Service Routine in ASM

UPDATE: No need to use GCC linker.


Most beginners tutorials in MCU's do not show how to set up and handle interrupts, and most of tutorials do not show how to code in ASM. Here is a complete tutorial showing how to blink a LED with STM32, set up IRQ and do that in ASM. Assembler is really simple and logic, just bare in mind that almost whatever you write in ASM will be translated straight to machine code. That's the whole secret.

First of all: I love FASM. It's very clear, simple and powerful. Programming huge, modern chips that run without any OS in ASM could be exhausting (there are almost no libraries and macro-instructions that could help out writing bare-metal code - maybe it should be created?) but it allows to understand how the chip works. Let's give it a try:

OS: Windows 7
Compiler: FASMARM download from here
Linker: LD from CodeSourcery none EABI GCC
Processor: STM32f103RB
Development board configuration: GPIOA P0 - button which couples to + VCC, P2 LED - hi state activates it.

Code:
 format elf                     ; Output format.  
 processor    37DA2FEh          ; Instruction encoding that fits our processor.  
 thumb                          ; This processor supports only thumb instruction set.  
 include 'vector_macro.inc'     ; Include file with some helpful macros dealing  
                                ; with vector table.  
 section ".text"  
 org 08000000h                  ; Offset address. Both this and placing code in  
                                ; section with an offset is necessary.  
 ; Begining of what goes into flash memory:  
         dw   Stack_Pointer            ; Stack pointer  
         vector _Start                 ; Reset vector  
         vector _NMI_handler           ; Some essential vectors  
         vector _HardFault_handler     ;  
         vector _MemManage_handler     ;  
         vector _BusFault_handler      ;  
         vector _UsageFault_handler    ;  
         default_handler 15            ; Skip 15 unused vectors  
         vector _EXTI_Line0            ; Our handler's address  

 _Start:  
         movlit r6, RCC_APB2ENR  
         movs r0, 4h                   ; GPIOA clock enable.  
         str r0, [r6]  

         movlit r6, GPIOA_CRL  
         movlit r0, 0x44444144         ; GPIOA config: P0 floating input  
         str r0, [r6]                  ; P2 - 10MHz output.

         movlit r6, EXTI               ; EXTI line 0 not masked.  
         movlit r0, 0x00000001  
         str r0, [r6]  

         movlit r6, (EXTI + 8)         ; EXTI0 rising trigger selected.  
         movlit r0, 0x00000001  
         str r0, [r6]  

         movlit r6, NVIC               ; Interrupt set enable.  
         movlit r0, 0x40  
         str r0, [r6]  

 ; Values stored in the GPIO output register:  
         movs r2, 0                    ; Turn off LED  
         movs r3, 4h                   ; Turn on LED  
         movlit r6, GPIOA_ODR          ; Point to PortA output data register.  

 loop:                                 ; Infinite loop. Turning the LED on and off.  
         str r2, [r6]                  ; Clear Port A, pin 2, turning on LED.  
         movlit r1, LEDDELAY  
 delay1:  
         subs r1, 1  
         bne delay1  
         str r3, [r6]                  ; Set Port A, pin 2, turning off LED.  
         movlit r1, LEDDELAY  
 delay2:  
         subs r1, 1  
         bne delay2  
         b loop  

 _EXTI_Line0:                          ; EXTI0 IRQ handler.  
         push {r6}                     ; Let's save whatever we modify on the stack.  
         push {r3}  
         push {r0}

         movlit r6, (EXTI + 0x14)      ; Clear pending IRQ. Otherwise the IRQ would be  
         movlit r0, 1                  ; permanently serviced.  
         str r0, [r6]  

         movlit r6, GPIOA_ODR          ; Point to PortA output data register.  
         str r3, [r6]                  ; Set Port A, pin 2, turning on LED.  

         add r9, 1                     ; When the exception takes place, r9 is being  
                                       ; incremented.  
         pop {r0}
         pop {r3}  
         pop {r6}  

         bx lr                         ; Branch to the address stored in link register.  

 _dummy:   
 _NMI_handler:   
 _HardFault_handler:   
 _MemManage_handler:   
 _BusFault_handler:   
 _UsageFault_handler:  
 _Default_handler:  
            add r0, 1                  ; In case of fault r0 is being incremented  
            b _dummy   
 ; Peripherals addresses:  
     GPIOA_CRL       =  0x40010800  
     GPIOA_ODR       =  0x4001080C  
     RCC_APB2ENR     =  0x40021018  
     EXTI            =  0x40010400  
     AFIO            =  0x40010000  
     NVIC            =  0xE000E100  

 ; Some config:  
     Stack_Pointer   =  0x20005000  
     LEDDELAY        = 1000000    

This is our code. First of all we put a couple of directives informing assembler how the code should be processed. We include a file with macro-definitions (it will be discussed later on). The very first thing that needs to be in the memory is the stack pointer. The second thing is the entry address where execution starts. First thing we do during the actual code execution is peripherals configuration. loop is an infinite loop that turns the led on and off. _EXTI_Line0 is a label defining our ISR.

Save the source in a file called example.ASM

File with some macro-definitions:
 macro vector name {dw name + 1}  
 macro default_handler repeat  
 {  
     rept repeat  
     {  
         dw _Default_handler + 1  
     \}  
 }       
 macro movlit reg,const {   
     local  .const,.skip   
     ldr   reg,[.const]   
     b    .skip   
     align  4   
   .const:   
     dw   const   
   .skip:   
 }  

Macro vector creates a label. Adding one to the address is essential cause in Thumb state every address loaded into memory must have LSB set. Macro default_handler creates a certain number of labels referring to a label named _Default_handler. It's necessary to skip unused vectors. Macro movlit is a way to load a const value into a register. Save the macro-definition file in the same place where the source file is and name it vector_macro.inc

Time for the linker script:
 SECTIONS  
 {  
     . = 0x08000000; /* start of flash */  
     .text : { *(.text)  }  
        
     .data :  
     {   
      *(.data)   
      *(.rom)  
     }  
      
     . = 0x20000000;  /*start of internal RAM */ 
     .ram : { *(.ram) }  
     .bss :  
     {  
      *(.bss)  
      *(.ram)  
     }   
 }                   

Save the linker script in link.ld

Compile the ASM source file. Your output is an object file called example.O Now we need to link the object file to create an executable file that could be loaded into flash memory. Use command:
$ arm-none-eabi-ld -v -T link.ld -o example.elf example.o 

Once we get the output file we can burn it. I'm using OpenOCD 0.5.0 and JTAG-lock-pick which is fully compatible with Amontec JTAGkey. Run OpenOCD with:
$ openocd -f /interface/jtagkey.cfg -f /target/stm32f1x.cfg

In a new console window connect with openocd server on port 4444:
telnet localhost 4444

Stop the core with:
reset halt

Move the example.elf file to your home folder and burn it in the flash memory:
flash write_image erase example.elf

After a successful download to chip you can start debugging the program. Button pressing causes External IRQ being serviced. To prove it reset the core:
reset

stop it:
halt

check the current register state:
reg

resume the core:
resume

Trigger the interrupt by pressing the button and stop the core one more time:
halt

Check the register state:
reg

you can see that R9 has been incremented during our ISR.

Use step command during halted to watch a single instruction being executed and use mdw address_number to read a word from a certain address. Have fun!

Update: 
To load the image without linking you need to change the line



 format elf


to:

 format binary as 'bin'


in the source file. Compile it with FASMARM. Fire up OpenOCD and load the image with:

flash write_image erase name_of_the_file.bin 0x8000000

The number in the command indicates the address where the flash memory begins - change it for different chips.

czwartek, 17 listopada 2011

ARM GCC Toolchain for Linux - Part 2.

Part 1.


IDE.
We'll use Eclipse. You can download it from www.eclipse.org. Be sure you choose the "Eclipse IDE for C/C++ Developers". Current release is Indigo. Eclipse does not require installation just un-tar it to the desired destination. Run Eclipse, enter path to the workspace, be careful it must not contain any whitespaces due to compiler problems. Mark that it's the default destination and confirm. On the next screen choose Workbench. Create a new project: File->New->C Project name it, choose the type: (Makefile project > Empty Project > -- Other Toolchain --) and click Finish. Before doing anything go to Project and unmark: Build Automatically and in Project->Clean unmark: Start a built immediately.

Let's tell Eclipse the path to Codesourcery's. Project->Properties->C/C++ Build->Settings chose GNU Elf Parser and input paths to specified files, e.g.:
/opt/sourcery/bin/arm-none-eabi-addr2line
/opt/sourcery/bin/arm-none-eabi-c++filt

There is a problem in Eclipse Indigo with system environment variables. We need to specify a path on our own. Project->Properties->C/C++ Build->Environment->Add name it "PATH". As the value put semicolon spaced paths to binutils and CS's compiler. Mine value looks like this:

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/sourcery/bin

Eclipse + OpenOCD + GDB.
To get it work we need two plugins. You can install add-ons with Help->Install new software.

The first one is "Eclipse C/C++ GDB Hardware Debugging". Go to Help->Install New Software. Input download directory to the proper dialog:
http://download.eclipse.org/tools/cdt/releases/indigo/
CDT Optional Features -> C/C++ GDB Hardware Debugging click Install.

The second one is Zylin. Download directory: http://opensource.zylin.com/zylincdt
select "Zylin Embedded CDT" click install. A couple months ago Zylin repository was abandoned but now everything seems to work well.

Running OpenOCD with Eclipse.
You can run external tools with Eclipse with one click of a button.
Run -> External Tools -> External Tools Configurations -> New
Name it depending of your config. IE: "JTAG-Pick-Lock + OpenOCD 0.5 + STM32"
Enter the path to OpenOCD. Mine is:
/usr/local/bin/openocd
Arguments:
-f interface/jtagkey.cfg -f target/stm32f1x.cfg
When finish click "Run".

The final step is creating a debugging profile, so you can enter debugging mode just with a click of the bug button. Go to:
Run -> Debug Configuration

Create a new configuration by double-clicking "GDB Hardware Debugging".Configuration is bounded to the project. You need to specify path to your project and specify executable that will be debugged. Last thing to do in this tab is changing GDB (DFS) Hardware Debugging Launcher. Click select other, mark "Use configuration specific setting" and choose "Standard GDB Hardware Debugging Launcher"

Go to tab named Debugger and specify path to CS's GDB. Mine path:
/opt/sourcery/bin/arm-none-eabi-gdb

Find a dialog box called "Port number" and enter value 3333

Go to tab Startup, unmark "Reset and Delay", unmark "Halt". Enter initialization commands to the dialog:
monitor reset halt

Mark "Set breakpoint at:" and enter
main
to the associated dialog.

Mark "Resume".

This is it. To start your debugging session click "Debug".
Have fun!

środa, 16 listopada 2011

ARM GCC Toolchain for Linux - Part 1.

This post is inspired by Freddie Chopin's tutorial showing how to install and configure a free IDE for programming/debugging ARM chips with no output limit. It's available on www.freddiechopin.info Since it's for Windows, a bit outdated and in Polish I decided to brief it here.

IMPORTANT UPDATE (17.02.2013): After completing reading the tutorial, before downloading any software please go to page http://www.freddiechopin.info/en/articles/35-arm/87-bleeding-edge-toolchain-o-co-chodzi and consider using Freddie's toolchain instead the one from CodeSourcery (Mentor).

System: Debian Squeeze 2.6.32-5-686.
JTAG-Pick-Lock (Amontec JTAG-Key)
Target: STM32

We'll be using Eclipse as IDE, Sourcery G++ Lite as toolchain, OpenOCD as debugger.

Codesourcery
Download from http://www.mentor.com/ Search for "Sourcery G++ Lite for ARM EABI". Since it's a bit tricky to find it, here is a direct link for current release: https://sourcery.mentor.com/sgpp/lite/arm/portal/release1802

Install with:
$ sudo sh

The installer will ask if you want to modify PATH. Confirm this. After installation check if it succeed. Create file main.c with simple code:


int main(void)
{
return 0;
}

Try to compile it with:
$ arm-none-eabi-gcc main.c

If you get something like:
/opt/sourcery/bin/../lib/gcc/arm-none-eabi/4.5.2/../../../../arm-none-eabi/bin/ld: warning: cannot find entry symbol _start; defaulting to 00008018

and compiler outputs file a.out everything should be ok.

IF your system PATH still isn't updated you need to edit /etc/profile. Add
export PATH=$PATH:/your/path/to/sourcery/bin
to the file. Go back to previous step to check if everything works.

OpenOCD
Current version of OpenOCD is 0.5.0. Download it form http://openocd.sourceforge.net/

Jtag-Pick-Lock (Amontec Jtag-Key) is FT2232 based. Basically you have to install libftdi-dev and compile OpenOCD with:
$ sudo ./configure --enable-ft2232_libftdi --enable-usbprog
$ make
$ make install

Smooth and easy. Check if it installed correctly:
$ openocd --version

Check if it works:
$ sudo openocd -f interface/jtagkey.cfg -f target/stm32f1x.cfg

OpenOCD should respond like this:


Open On-Chip Debugger 0.5.0 (2011-11-15-22:39)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.berlios.de/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'jtag'
1000 kHz
adapter_nsrst_delay: 100
jtag_ntrst_delay: 100
cortex_m3 reset_config sysresetreq
Info : clock speed 1000 kHz
Info : JTAG tap: stm32.cpu tap/device found: 0x3ba00477 (mfg: 0x23b, part: 0xba00, ver: 0x3)
Info : JTAG tap: stm32.bs tap/device found: 0x16410041 (mfg: 0x020, part: 0x6410, ver: 0x1)
Info : stm32.cpu: hardware has 6 breakpoints, 4 watchpoints


If it does work, you need to add a rule that will allow regular users to use the dongle. Create a file:
/etc/udev/rules.d/45.jtagkey.rules
that contains:
SYSFS{idVendor}=="0403", MODE="666", GROUP="users"
(vendor depends on JTAG you use). Save and add your user to the chosen group.

Now you can debug from regular user's account, yay!

poniedziałek, 19 września 2011

Embedded programming - how to choose a target processor

Purpose of this blog is not a step by step tutorial, therefore I won't make a tutorial about uC's programming (besides I'm a beginner as well). If you want to program uC's I'll give you some advices:
- First of all, don't ask me or communities questions like: "What's a debugger". Most questions are answered thousands times, you just need to do the search, if you ask, you'll just be another lazy, annoying newbie.
OK. Real advices:
- Learn C programming language, its simple and efficient. "ANSI C" by Brain Kernighan and Dennis Ritchie is a good point to start. Go through this book before buying a MCU dev kit.
- Find out which uC suits you best:

  • Atmel's AVR is very popular, there is a lot of support, there is a free IDE (from Atmel?): Avr Studio (C, ASM, C++), programmer devices are simple and cheap, there are tons of sample projects, schematics, examples on the internet. A great advantage is BASIC compiler for most AVRs (called Bascom). BASIC is a great programmer's time saver in some applications, but in some cases produces slow and large output. Bascom isn't free, but there is some kind of evaluation. Avr is an 8bit uC. And it's good for beginners. Cont's of AVR:
  1. Atmel for unknown reasons, discontinued for a couple of month's production of world's most popular hobby MCU's. That increased AVR's prices incredibly. Atmel is an unreliable manufacturer.
  2. Programming devices are cheap and simple, but programming-debugging devices are quite complicated and aren't that cheap. You will need a debugger sooner or later.
  3. To be honest AVRs are becoming out of date.
  • On the other hand very popular are MCUs based on ARM core. These units are 32bits, have tons of peripherals and are a nightmare for beginners. Most manufacturers don't provide a free IDE, some have very poor support. If you choose an ARM based MCU, you should have some knowledge about uC's. Debuggers are very expensive and in fact essential. If you already know something about programming, choose it. (Later you'll have to choose between the manufacturer and between core version).
  • My advice for beginners is to choose Texas Instruments MSP430 16bit uC's. It's cheap - search for TI Launchpad dev board, this board has an integrated programmer-debugger. TI provides an Eclipse based IDE that is limited to 16k output code, but 16k is quite a lot of code. Bet you won't be able to fill a 2k space for a long time ;) MSP430 has a big community, a marvelous and clear support, you don't need anything else. TI is an excellent choice for a beginner.
  • There are many other kinds of MCU's. Microchip PIC and Motorola are very popular, but I have no experience about it. Worth mentioning is that there are thousands kinds of PIC's and that Megasquirt is based on Motorola MCU.
  • Summary: ARM based uCs seem to be the future in very advanced systems. TI offers a variety of MSP430 uCs from simple units to quite powerful ones.
- Get familiar with Assembler. It's useful. I don't mean, try to write complicated algorithms in ASM, just get familiar with it's commands and syntax. It will help you figure out how MCU works.

My gear:
  • TI Launchpad MSP430 + CCS
  • ARM Cortex M3 STM32 (STM32F103RBT) Attached to a simple dev board with fancy TFT Touch Screen (which I haven't used once ;). I must warn you, that ST provides awful and poor support. Perhaps NXP or TI would be a better choice - I'll consider this if I need a change.
  • A simple DIY contact board with couple of switches, a 2x16 HD47780 Lcd and transistors attachable for both TI and STM32 devboards.
  • ARM programmer-debugger: A copy of Amontec JTAG-Key.
  • IDE: Eclipse + Codesourcery Toolchain + OpenOCD. Very useful and totally free with no limitations.
I think that's all for today.
Cheers.


Update:

Recently I got a new devboard from ST STM32F4 and a copy of TI's BeagleBoard - DevKit8000 equipped with a Cortex A8 processor. It's a powerful processor with a huge core designed to run under control of an OS rather than bare metal like other MCUs I mentioned in this post. I am trying to make use of it.