D21XPLAIN - simple program to get started

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Greets,

 

I am continuing on my quest to get an actual ARM program going, but quite frankly I am scratching my head over it.

 

I used Atmel START and created my device - configured PortB0..7 as outputs that are low on start up.  Named the pins in START data_0..data_7.

 

8MHZ clock feeding the CPU.

 

I was able to build the project and load the configuration that START created and the data_0...data_7 pins are indeed low.

 

Now in my AVR world if I want to make a port a certain value I can just do this:

while(1)
{
    DDRB = 0xff;
    PORTB = 0xAA;   //set alternating pins high
}

and I am done.

 

to do a counter with a half second delay between counts I can do:

uint8_t  count = 0x00;
while(1)
{
    count++;
    PORTB = count;
    _delay_ms(500);
    
}

and port B will increment by one every half second.

 

But in the world of ARM, this does not seem so simple.

in my pins.h file I see my port configured:

#ifndef ATMEL_START_PINS_H_INCLUDED
#define ATMEL_START_PINS_H_INCLUDED

#include <hal_gpio.h>

#define data_4  GPIO(GPIO_PORTB, 4)
#define data_5  GPIO(GPIO_PORTB, 5)
#define data_6  GPIO(GPIO_PORTB, 6)
#define data_7  GPIO(GPIO_PORTB, 7)
#define data_0  GPIO(GPIO_PORTB, 0)
#define data_1  GPIO(GPIO_PORTB, 1)
#define data_2  GPIO(GPIO_PORTB, 2)
#define data_3  GPIO(GPIO_PORTB, 3)

#endif // ATMEL_START_PINS_H_INCLUDED

and I also see that you have to configure each pin INDIVIDUALLY(cannot simply make the whole bunch an output with one simple command)

here is an example for two pins:

// GPIO on PB04

	// Set pin direction to output
	gpio_set_pin_direction(data_4, GPIO_DIRECTION_OUT);

	gpio_set_pin_level(data_4,
	        // <y> Initial level
	        // <id> pad_initial_level
	        // <false"> Low
	        // <true"> High
			false);

	gpio_set_pin_mux(data_4, GPIO_MUX_OFF);

	// GPIO on PB05

	// Set pin direction to output
	gpio_set_pin_direction(data_5, GPIO_DIRECTION_OUT);

	gpio_set_pin_level(data_5,
	        // <y> Initial level
	        // <id> pad_initial_level
	        // <false"> Low
	        // <true"> High
			false);

	gpio_set_pin_mux(data_5, GPIO_MUX_OFF);

again, a lot of work.

 

But OK, if thats how it's done then great that I have START for this.  But after that how do I get a count to update the pins all at the same time?

I have this:

#include "atmel_start.h"
#include "atmel_start_pins.h"

int main(void)
{
	system_init();
	uint8_t  count = 0x00;
	
	/* Replace with your application code */
	while(1) {
				count++;
				
		//now what do I do to see the value of count on teh pins I configured?
		//and how do I create a delay?  
	}
}

and nothing else.

 

Any help is appreciated.

 

Lastly, I looked at the output for the "simple" configuration and this is what it needed from the ARM:

    "C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\arm\arm-gnu-toolchain\bin\arm-none-eabi-size.exe" "SmartStart0.elf"
           text       data        bss        dec        hex    filename
           1532          0       8336       9868       268c    SmartStart0.elf
    Done executing task "RunCompilerTask".
    Using "RunOutputFileVerifyTask" task from assembly "C:\Program Files (x86)\Atmel\Studio\7.0\Extensions\Application\AvrGCC.dll".
    Task "RunOutputFileVerifyTask"
                Program Memory Usage     :    1532 bytes   0.6 % Full
                Data Memory Usage         :    8336 bytes   25.4 % Full
    Done executing task "RunOutputFileVerifyTask".

8.5k, or 25% of my SRAM to configure the damned thing?  Seriously?

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, some more reading of the D21 datasheet and I came across this:

 

WHat has me intrigued is the OUT register(s) which may be what I am looking for.  I am trying to figure out how to access those registers as they are grouped in 8bit slices.

 

But if we go back to my OP, there must be a way to group the pins I showed.

 

JIm

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can achieve this very similar but Atmel made the ports 32-bit wide in the header files.

#include "sam.h"

 /* Byte address for OUT[7:0] */
#define PORTB_B0  (*(volatile uint8_t *)0x60004490UL)

void __delay_ms ( uint32_t n )
{
  while ( n-- )
  {
    /* reset the timer */
    SysTick->VAL = 0;
    /* divide up in blocks of 1ms */
    if ( n > 0 )
    {
      while ( ! ( SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk ) )
      {
        /* blocking, wait for time to expire */
      };
    }
  }
}

int main ( void )
{
  uint8_t  count = 0x00;

  /* Change the clock prescaler so the CPU is running at 8MHz */
  SYSCTRL->OSC8M.bit.PRESC = 0;
  /* count value that should result in 1 msec timeout from 8MHz CPU clock */
  SysTick->LOAD = 8000 - 1;
  /* run from CPU clock, enable timer */
  SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;

  /* PB00 to PB07 as outputs */
  PORT->Group[1].DIRSET.reg = 0x000000FF;

  while ( 1 )
  {
    count++;
    /*  this will change all port B pins that are configured as outputs */
    PORTB_B0 = count;
    __delay_ms ( 500 );
  }
}

Changing the stack size in the linker, optimization -Os, and using nano.specs

"C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\arm\arm-gnu-toolchain\bin\arm-none-eabi-size.exe" "port8bitAccess.elf"
           text       data        bss        dec        hex    filename
            684          0        544       1228        4cc    port8bitAccess.elf
    Done executing task "RunCompilerTask".
    Task "RunOutputFileVerifyTask"
                Program Memory Usage     :    684 bytes   0.3 % Full
                Data Memory Usage         :    544 bytes   1.7 % Full
    Done executing task "RunOutputFileVerifyTask".

Edited Dec 31

Last Edited: Thu. Dec 31, 2015 - 02:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

THanks!  I will look at this in depth and post back.

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK THAT WORKS!

 

Thanks cwunder.

 

Now I have some questions, so bear with me.

 

#include "sam.h"

I would have thought that the START.ATMEL wizard would have added that.  Wonder why it did not.

 

Do I have to add the other standard C libraries like STDIO.h, STDINT.h, MATH.h and the like?  I am guessing yes?

 

/* Change the clock prescaler so the CPU is running at 8MHz */
	  SYSCTRL->OSC8M.bit.PRESC = 0;

When I ran the wizard I selected the 8MHz as the clock.  After I ran this code, I commented the line out and the code ran at the same speed so is it safe to assume I got this right?

/* count value that should result in 1 msec timeout from 8MHz CPU clock */
	  SysTick->LOAD = 8000 - 1;
	  /* run from CPU clock, enable timer */
	  SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;

This has me puzzled, and I have no way of explaining my question(s) so I ask can you explain what is going on here?  What Timer? 

  /* PB00 to PB07 as outputs */
	  PORT->Group[1].DIRSET.reg = 0x000000FF;

Can you explain where you got the "Group[1]from?  I understood the DIRSET.reg part.

 

 /*  this will change all port B pins that are configured as outputs */
		  PORTB_B0 = count;

I would have guessed this to have been:

 /*  this will change all port B pins that are configured as outputs */
		  PORTB = count;

But, From my reading of the datasheet, and the page I posted above I do not understand how:

 /* Byte address for OUT[7:0] */
 #define PORTB_B0  (*(volatile uint8_t *)0x60004490UL)

works.  Specifically where the

0x60004490UL

came from.

 

Now this:

  __delay_ms ( 500 );

I know it is calling the following:

void __delay_ms ( uint32_t n )
 {
	 while ( n-- )
	 {
		 /* reset the timer */
		 SysTick->VAL = 0;
		 /* divide up in blocks of 1ms */
		 if ( n > 0 )
		 {
			 while ( ! ( SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk ) )
			 {
				 /* blocking, wait for time to expire */
			 };
		 }
	 }
 }

with the value(500) being the amount of milliseconds to sit in this loop.  What I do not understand is the

Systick->VAL = 0 

and:

while ( ! ( SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk ) )

Again, where did SysTick->val, systick->CTRL, and SysTick_CTRL_COUNTFLAG_Msk come from?

 

My apologies for the many and seemingly ridiculous questions, but this is new territiry for me.

 

Jim

 

Edit:

And this is what I get for my output after the build:

   text       data        bss        dec        hex    filename
           1600          0       8336       9936       26d0    SmartStart0.elf
    Done executing task "RunCompilerTask".
    Task "RunOutputFileVerifyTask"
                Program Memory Usage     :    1600 bytes   0.6 % Full
                Data Memory Usage         :    8336 bytes   25.4 % Full
    Done executing task "RunOutputFileVerifyTask".

It appears that possibly using the START.ATMEL wizard is adding a lot of overhead compared to what your build report says.

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

Last Edited: Sat. Jan 2, 2016 - 03:21 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The cortex m0 has two peripherals - nvic (interrupt controller) and systick. The systick is a simple timer for generating a system tick. Then the manufacturer adds their own.

The addresses come from the datasheet. Each peripheral has a base address, then you add an offset for the required register.

Last Edited: Sat. Jan 2, 2016 - 01:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Jim,

 

Some background.

 

It is stated the Cortex M0+ was designed by ARM to compete with existing 8/16-bit microcontrollers. Silicon manufacturers differ on what they implement from the Core options in their silicon, you can find a table in the datasheet (see 10.1.1 Cortex M0+ Configuration). In addition they you have to see what access is available for the peripherals, Atmel in a lot of cases implemented byte, half-word, and word access to their peripherals. Another software standard for Cortex vendors is is they implement CMSIS (Cortex Microcontroller Software Interface Standard). From this there should be a instance and component header file for each peripheral.

 

To your questions:

#include "sam.h"

I would have thought that the START.ATMEL wizard would have added that.  Wonder why it did not.

I don't use ASF or Atmel Start since I don't agree with their implementations. The projects that I create are bare metal using the GCC C Executable Project from Atmel Studio which has a framework to basic CMSIS implementation.

Do I have to add the other standard C libraries like STDIO.h, STDINT.h, MATH.h and the like?  I am guessing yes?

Yes if you need these files or libraries

/* Change the clock prescaler so the CPU is running at 8MHz */
	  SYSCTRL->OSC8M.bit.PRESC = 0;

When I ran the wizard I selected the 8MHz as the clock.  After I ran this code, I commented the line out and the code ran at the same speed so is it safe to assume I got this right?

I don't use Start, the default clock for the D21 is the internal 8MHz oscillator with the prescaler enabled such that the CPU is running from 1 MHz.

/* count value that should result in 1 msec timeout from 8MHz CPU clock */
	  SysTick->LOAD = 8000 - 1;
	  /* run from CPU clock, enable timer */
	  SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;

This has me puzzled, and I have no way of explaining my question(s) so I ask can you explain what is going on here?  What Timer? 

The SysTick timer is a 24-bit down counter that is part of the Cortex processor.

 

SysTick->LOAD  is the SYST_CVR.RELOAD register. Given the FCPU is egual to 8MHz to get milliseconds you need to divide by 1000 so you end up with 8000 as the value. You need to subtract one as stated by ARM below.

 

Calculating the RELOAD value
The RELOAD value can be any value in the range 0x00000001-0x00FFFFFF. You can program a value of 0, but this has no effect because the SysTick exception request and COUNTFLAG are activated when counting from 1 to 0.

To generate a multi-shot timer with a period of N processor clock cycles, use a RELOAD value of N-1. For example, if the SysTick interrupt is required every 100 clock pulses, set RELOAD to 99.

 

SysTick->CTRL is the SYST_CSR register and the define comes from the core_cm0plus.h file. This is enabling the SysTick timer.

  /* PB00 to PB07 as outputs */
	  PORT->Group[1].DIRSET.reg = 0x000000FF;

Can you explain where you got the "Group[1]from?  I understood the DIRSET.reg part.

This comes from the header file for the Component description for PORT. Atmel defined the ports as 32-bit access or bit access and did not define access for byte or halfword (in the header file). So for Port B you need the PORT->Gorup[1] to address any register for Port B, Port A would be PORT->Gorup[0]. Atmel implemented the single-cycle port access bus so there are two ways to address the PORT peripheral. PORT or PORT_IOBUS.

#define PORT                          (0x41004400UL) /**< \brief (PORT) APB Base Address */
#define PORT_IOBUS                    (0x60000000UL) /**< \brief (PORT) IOBUS Base Address */

But, From my reading of the datasheet, and the page I posted above I do not understand how:

 /* Byte address for OUT[7:0] */
 #define PORTB_B0  (*(volatile uint8_t *)0x60004490UL)

works.  Specifically where the

0x60004490UL

came from.

From the datasheet the OUT register is calculated from 0x10+x*0x80 [x=0..2] (X implies the Port). So for Port B this becomes 0x10+1*0x80 which equals offset 0x90.

 

While the code will work because the address decoding. To be clear and correct the address for a 8-bit access to the Port B peripheral for bits[7:0] The correct value for the device for Byte access is:

/* Byte address for OUT[7:0] */
#define PORTB_B0  (*(volatile uint8_t *)0x60000090UL)

Concerning the SysTick here is the information from the ARM manual.

When enabled, the system timer counts down from the reload value to zero, reloads (wraps to) the value in the SYST_RVR on the next clock cycle, then decrements on subsequent clock cycles. Writing a value of zero to the SYST_RVR disables the counter on the next wrap. When the counter transitions to zero, the COUNTFLAG status bit is set to 1. Reading SYST_CSR clears the COUNTFLAG bit to 0.Writing to the SYST_CVR clears the register and the COUNTFLAG status bit to 0. The write does not trigger the SysTick exception logic. Reading the register returns its value at the time it is accessed.
 

While I am correcting, the SysTick->VAL = 0; statement outside the loop. This is only needed once to clear the timer. Also this will work when the delay is 1.

void __delay_ms(uint32_t n)
{
  /* reset the timer once to sync up the delay */
  SysTick->VAL = 0;

  do
  {
    /* divide up in blocks of 1ms */
    if (n > 0)
    {
      while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk))
      {
        /* blocking, wait for time to expire */
      };
    }
  }
  while (n--);
}

Concerning the code generation you can reduce the flash size by using the --specs=nano.specs option it replaces the standard C library implementations with more space optimized libraries. You need to verify if your application can do use them and save program memory.

 

 

Hope that helps.

-Chris

Last Edited: Sat. Jan 2, 2016 - 04:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Russell and Chris,

 

Chris,

I am going to have to digest your code and read the D21 manual at the same time.  THis is great stuff and a solid start in learning about these devices that I feel I am going to be forced to use.

 

Expect some more questions smiley

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user