Discussion in "ARM Development" started by    HowNotToEngineer    Jun 8, 2014.
Sun Jun 08 2014, 06:36 am
#1
Hi,
I am starting to develop some code for my new Arduino Due, my first test has been to toggle an LED using timer interrupts without using the Arduino or ASF libraries.
After digging through the datasheet I have ended up with the code below that works a treat when compiled and uploaded using the Arduino IDE, the led blinks happily on and off.

I am now attempting to get the exact same code to compile and upload using Atmel Studio 6.1, I have started a new C++ project for the SAM3X8E device and setup an external tool as follows (the bat file is attached for anyone interested)

Command: C:\Program Files (x86)\arduino-1.5.6-r2\hardware\tools\bossac.bat
Arguments: -i --port=COM8 -U false -e -w -v -b "$(ProjectDir)Debug\main.bin" -R

The code compiles and uploads just fine, I can even turn the LED on or off using the dWrite macro. But for some reason the timer interrupt does not seem to work, after uploading the LED does not blink as it should.

I did get this code working in Atmel Studio 6 with the external tool above by creating a project using the Arduino Due ASF template, the only problem with this is that I would like to use C++.

So from my testing I think my code is ok because it works using the Arduino IDE, I think my external tool setup is ok because I can get the code to work using the Due ASF template in AS6.
What I suspect might be the issue is the compiler and or linker setup when using the AS6 C++ project.

Can anyone offer any hints or advice on how I might be able to get my interrupt running using an AS6 C++ project?
Thanks in advance!

HNTE_ARM_lib.h
#include "sam.h"

#define setAsOutput(x) _setOutput(x)
#define _setOutput(x, b) PIO##x->
PIO_OER |= (1<<b)

#define dHigh(x) _digitalHigh(x)
#define _digitalHigh(x, b) PIO##x->
PIO_SODR |= (1<<b)

#define dLow(x) _digitalLow(x)
#define _digitalLow(x, b) PIO##x->
PIO_CODR |= (1<<b)

#define dWrite(x, val) (val ? _digitalHigh(x) : _digitalLow(x))


main.cpp
#include "HNTE_ARM_lib.h"
#define LED B,27

volatile uint8_t interrupt_flag;

int main(void){
    SystemInit();

    //setup the due's onboard led
	setAsOutput(LED);
	dWrite(LED, 1);
	
	//disable pmc registers write protect and power up the clock source
	PMC->PMC_WPMR = (uint32_t)0x504D43;
	PMC->PMC_PCER0 |= (1<<TC3_IRQn);
	
	//disable timer registers write protect
	TC1->TC_WPMR = (uint32_t)0x54494D;

	//setup the timer in waveform mode, reset at RC, 128 x divider 
	TC1->TC_CHANNEL[0].TC_CMR = TC_CMR_WAVE|TC_CMR_WAVSEL_UP_RC|TC_CMR_TCCLKS_TIMER_CLOCK4;
	
	//1ms timer val
	uint32_t rc = F_CPU/128ul/1000ul;
		
	//init RC and RA
	TC1->TC_CHANNEL[0].TC_RC = rc;
	TC1->TC_CHANNEL[0].TC_RA = rc>>1;
	
	//enable timer and trigger to start timer
	TC1->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;

	//enable interrupt on overflow
	TC1->TC_CHANNEL[0].TC_IER = TC_IER_CPCS;
	NVIC_EnableIRQ(TC3_IRQn);

	static uint32_t counter;
	static uint8_t led_on;

	while(1){
		if(interrupt_flag){
			//toggle the led every 1000ms
			if(++counter >= 1000){
				dWrite(LED, led_on);
				led_on ^= 1;
				
				counter = 0;
			}
			
			interrupt_flag = FALSE;
		}
	}	
}

void TC3_Handler(void){
	//clear the timer interrupt
	volatile uint32_t clear = TC1->TC_CHANNEL[0].TC_SR;
	
	interrupt_flag = TRUE;
}


[ Edited Sun Jun 08 2014, 06:41 am ]
Mon Jun 09 2014, 10:33 pm
#2
I'd start by simplifying where possible.
Move the LED switching to the interrupt handler and keep it simple.
such as...
void TC3_Handler(void){
static uint32_t counter=0;

	//clear the timer interrupt
	uint32_t clear = TC1->
TC_CHANNEL[0].TC_SR;  

      counter++;      
       if(counter  < 1000)
        LED= off;  //Not a real command .. just means put Led off using most direct method.

       if(counter >
 =1000)
       LED= on;  //put Led on using most direct method.

       if(counter >
 2000)
      counter =0;
}      


I would not use macros as they may not work as expected on all platforms.


[ Edited Mon Jun 09 2014, 10:39 pm ]
Wed Jun 11 2014, 08:18 pm
#3
Thanks for the reply,
I have had a play removing macros and playing about a bit simplifying code just in case, but I am pretty sure the issue lies somewhere else.

Using this exact code I can get it all to work in an Atmel Studio 6 C++ project by just adding the ASF generic board support driver through the ASF wizard.
For now I am happy with this solution, I don't have to use any of the ASF libraries and I can compile C++ code.

Although it is a little annoying that I can't figure out why using the ASF startup files works when the default project startup files don't.
Comparing the startup_sam3x.c file imported by a blank c++ project to the startup_sam3x.c file imported when I add the ASF generic board driver there are several differences in how the interrupt vector table is defined.
My knowledge of embedded systems starts to run out at this point so I am not sure if this is a clue or not.

Anyway, if anyone has any ideas it would be cool to figure it out.
If not I have a project that compiles and code that works so I am happy..

Cheers!
Thu Jun 12 2014, 11:00 am
#4
Can you attach both the startup files? Maybe I can go through them and get some answers? You can expect differences in the compilers from version to version. Its always good to go through release notes and compiler manual once.
Sat Jun 14 2014, 07:13 am
#5
Hi Ajay,
Thanks for the reply.
I have figured it out...
There seems to be a mistake in the definition of the interrupt vector table when creating an embedded C++ project for the SAM3X8E in Atmel Studio 6.
The interrupt vector table is defined In startup_sam3xa.c, the vector table seems to be missing vector 9, it goes
.
.
.
    (void*) UART_Handler,   /* 8  Universal Asynchronous Receiver Transceiver */
#ifdef _SAM3XA_SDRAMC_INSTANCE_
    (void*) SDRAMC_Handler, /* 10 Synchronous Dynamic RAM Controller */
#else
    (void*) (0UL),          /* 10 Reserved */
#endif /* _SAM3XA_SDRAMC_INSTANCE_ */
.
.
.


This means that all of the interrupts below the UART handler are offset by 1, to fix this I just added the following lines to the interrupt table.

.
.
.
    (void*) UART_Handler,   /* 8  Universal Asynchronous Receiver Transceiver */
#ifdef _SAM3XA_SMC_INSTANCE_
    (void*) SMC_Handler,     /*  9  SMC */
#else
    (void*) (0UL),           /*  9 Reserved */
#endif /* _SAM3XA_SMC_INSTANCE_ */
#ifdef _SAM3XA_SDRAMC_INSTANCE_
    (void*) SDRAMC_Handler, /* 10 Synchronous Dynamic RAM Controller */
#else
    (void*) (0UL),          /* 10 Reserved */
#endif /* _SAM3XA_SDRAMC_INSTANCE_ */
.
.
.


Hopefully this will help anyone else out there with the same issue.
Cheers!
Nick

 ajay_bhargav like this.
Sat Jun 14 2014, 07:59 am
#6
This issue seems to have been fixed in Atmel Studio 6.2.
Cheers.
Mon Jun 16 2014, 10:34 am
#7
Thank you so much for notifying

Get Social

Information

Powered by e107 Forum System

Downloads

Comments

Bobbyerilar
Thu Mar 28 2024, 08:08 am
pb58
Thu Mar 28 2024, 05:54 am
Clarazkafup
Thu Mar 28 2024, 02:24 am
Walterkic
Thu Mar 28 2024, 01:19 am
Davidusawn
Wed Mar 27 2024, 08:30 pm
Richardsop
Tue Mar 26 2024, 10:33 pm
Stevencog
Tue Mar 26 2024, 04:26 pm
Bernardwarge
Tue Mar 26 2024, 11:15 am