Show Posts
Pages: [1] 2
1  Products / Arduino Due / Re: which data type can maintain 16 bits in Due? on: July 29, 2013, 08:56:15 am
You can restrict the variable to 16 bits by using the int16_t or uint16_t (unsigned integer) types.
2  Products / Arduino Due / Re: Due Servo Pins on: July 08, 2013, 08:18:43 am
If you use the hardware servo library (http://forum.arduino.cc/index.php?topic=130631.0) you can attach up to eight servos at once. This method uses an independent clock and will have no effect on the timers or functions like millis().
3  Products / Arduino Due / Re: Programming Due with Matlab/Simulink on: June 25, 2013, 06:28:31 am
As far as i know, MATLAB/Simulink doesnt generate code directly for the due. The arduino has a lot of hidden initialization code that you don't see in the arduino IDE, which will all be missing in the autogenerated code. The generated .c file will also contain a main() function that will conflict with the already existing arduino main() function.

Regardless of whether you use the arduino IDE, atmel studio or some other IDE you will have to manually edit the generated code, maybe into a library that can be included by the preprocessor (i.e., #Include "library.h"). Personally, I usually just cut and paste some parts from the generated code into mine, like complicated math an array/matrix operations that I don't want to type by hand.
4  Products / Arduino Due / Re: TC and PWM on: June 22, 2013, 04:02:02 pm
1) I can confirm that the TC counter is in fact 32bit so the data sheet must be a typo.

2) I'm not totally sure about those registers but I am pretty sure the sam3x registers start at the LSB so the "first" 16 bits should be numbers 0 through 15
5  Products / Arduino Due / Re: Guys plz help . on: June 20, 2013, 02:03:21 pm
It is all pretty self-explanatory. Download the BOSSA program from the link I provided. BOSSA has a GUI for windows that let's you select the serial port to use, the binary file to upload and a few other options that you don't need to mess with. Select whatever port your Arduino shows up as and then you just press the button that says "write".
6  Products / Arduino Due / Re: Guys plz help . on: June 20, 2013, 11:30:28 am
The Arduino IDE 1.5.x includes an application called BOSSA (you can download it separately here: http://www.shumatech.com/web/products/bossa) and uses it to upload the compiled binaries. I've used Makefiles to compile code for and Due and then used BOSSA from the command line to upload the final .bin/.hex to the board.
7  Products / Arduino Due / Re: timer interrupt on: June 13, 2013, 11:28:10 am
I'm not sure what exactly happens when interrupt routines start conflicting. Normally you keep the interrupt code as short as possible. If you are trying to check the frequency of the timer you need to use faster external equipment (e.g., oscilloscope). Otherwise you really can't print messages at rates that high.

Your example code has a few problems. First you only need to call Serial.begin() once, starting it during every interrupt just wastes time. You also use a timer frequency of: 86000000 Hz / 128 / 4 = 168000 Hz and are trying to send messages using a 156200 baud (i.e. 156200 characters per second). If the interrupt overrides itself then I would only expect to see 1 or 2 characters sent through serial.

Also delay() doesn't disable interrupts, so it could be that TC3_Handler() gets called whenever you use delay(1)?


8  Products / Arduino Due / Re: [SOLVED] Non-blocking PWM duty cycle capture using Timer Counter on: June 11, 2013, 08:42:57 pm
@NielsJL: You are very welcome, I'm glad you found it useful.

I don't think it would be too hard to modify the code to capture a PPM signal instead of just PWM. Maybe if you used an array to store the duration of all 6? 8? pulses, loading new values on every falling edge. The only challenge would be to know which pulse corresponds to which channel and detecting the end of each PPM frame.
9  Products / Arduino Due / Re: ADXL345 Accelerometer on I2C Arduino Due on: June 07, 2013, 11:47:37 pm
It would be easier to help if you could post your code. Or are you directly copy and pasting from the tutorial you linked? Do you think it is possible that the issue is caused because the Due uses 32 bit int's?

I know you said you had to use i2c but the following SPI example works on the Due and may be of some help:
Code:
/*
  Calibrate ADXL345
 
  Use this sketch to get offset and scaling factor to calibrate the ADXL345.
  Orient the sensor so that one of the six axes is aligned with gravity, type
  the character 'r' in the serial terminal to reset the min/max values, then
  slowly move the sensor around until the values stop increasing/decreasing.
  Use those values to calculate the proper scaling and offsets.
*/


// pins 4, 10, and 52 allow auto chip selection on the Due
#define ACCEL_SS_PIN  10 // SPI slave select pin for accel

// general SPI defines
#define READ   0x80
#define WRITE  0x00
#define MULTI  0x40
#define SINGLE 0x00

#include <SPI.h> // built in SPI library

uint8_t accel_buffer[6];
int16_t accel_raw[3];
int16_t accel_max[3] = {0,0,0};
int16_t accel_min[3] = {0,0,0};

void setup() {
  Serial.begin(115200);
  Serial.println("Serial communication initialized...");
 
  // initialize the SPI bus for the accelerometer
  SPI.begin(ACCEL_SS_PIN);
  SPI.setBitOrder(ACCEL_SS_PIN, MSBFIRST);
  SPI.setDataMode(ACCEL_SS_PIN, SPI_MODE3);
  // ADXL345 max clock speed is 5 MHz
  SPI.setClockDivider(ACCEL_SS_PIN, (F_CPU / 5000000L) + 1);
 
  // make sure that the device is connected
  SPI.transfer(ACCEL_SS_PIN, READ | SINGLE | 0x00, SPI_CONTINUE);
  uint8_t accel_name = SPI.transfer(ACCEL_SS_PIN, 0x00);
 
  if(accel_name != 0xE5) {
    Serial.println("ADXL345 not detected!");
    Serial.print(accel_name, HEX); Serial.println(" found, should be 0xE5");
    delay(1000);
  }
 
  // Register 0x2C: BW_RATE
  // use 2X oversampling with 400 Hz ODR (0x0C)
  // 800 Hz ODR (0x0D) is also available
  SPI.transfer(ACCEL_SS_PIN, WRITE | SINGLE | 0x2C, SPI_CONTINUE);
  SPI.transfer(ACCEL_SS_PIN, 0x0C);
 
  // Register 0x2D: POWER_CTL
  // put into measurement mode (0x08)
  SPI.transfer(ACCEL_SS_PIN, WRITE | SINGLE | 0x2D, SPI_CONTINUE);
  SPI.transfer(ACCEL_SS_PIN, 0x08);
 
  // Register 0x31: DATA_FORMAT
  // full resolution (0x08) at range of +/- 16 g (0x03)
  SPI.transfer(ACCEL_SS_PIN, WRITE | SINGLE | 0x31, SPI_CONTINUE);
  SPI.transfer(ACCEL_SS_PIN, 0x08 | 0x03);
 
  delay(100);
}


void loop() {
 
  // multibyte burst read of data registers (from 0x32 to 0x37)
  SPI.transfer(ACCEL_SS_PIN, READ | MULTI | 0x32, SPI_CONTINUE);
 
  for(int i = 0; i < 5; i++) {
    accel_buffer[i] = SPI.transfer(ACCEL_SS_PIN, 0x00, SPI_CONTINUE);
  }
  accel_buffer[5] = SPI.transfer(ACCEL_SS_PIN, 0x00);
 
  // combine the raw data (ADXL345 sends LSB first by default)
  //             _________ MSB ________   _____ LSB _____
  accel_raw[0] = (accel_buffer[1] << 8) | accel_buffer[0];
  accel_raw[1] = (accel_buffer[3] << 8) | accel_buffer[2];
  accel_raw[2] = (accel_buffer[5] << 8) | accel_buffer[4];
 
  for(int i = 0; i < 3; i++) {
    if(accel_raw[i] > accel_max[i]) {
      accel_max[i] = accel_raw[i];
    }
    if(accel_raw[i] < accel_min[i]) {
      accel_min[i] = accel_raw[i];
    }
   
    Serial.print(accel_min[i], DEC); Serial.print("/");
    Serial.print(accel_max[i], DEC); Serial.print("\t");
  }
  Serial.println();
 
  // listen to serial for a reset signal (character 'r')
  if(Serial.available() > 0) {
   
    int val = Serial.read();
   
    if(val == ((int) 'r')) {
      for(int i = 0; i < 3; i++) {
        accel_min[i] = 0;
        accel_max[i] = 0;
      }
    }
  }
 
  delay(50);
}
 
10  Products / Arduino Due / Re: PWM Duty Cycle Capture using Timer Counter on: May 25, 2013, 09:07:16 am
Registers TC_RA and TC_RB are actually capture registers, at least when the TC is put into capture mode. Setting TC_CMR_LDRA and TC_CMR_LDRB dictate when the registers capture the counter value based on the TIOA signal behaviour. In my original example I use TC_CMR_LDRA_FALLING, so every time the TIOA signal falls the current counter value is stored in register TC_RA. This way you never need to actually read the counter value directly.

I seem to have a working sketch now. It incorporates some code from the Atmel Software Framework, mainly the direct register manipulation.

The basic working principle is that a timer counter (TC) is put into capture mode. The TC is told to load capture register A (TC_RA) on the rising edge of a PWM signal and capture register B (TC_RB) on the falling edge. The number of counts that the signal is active for is equal to TC_RB - TC_RA. An interrupt routine is run every time TC_RB is loaded and saves the register values to variables. The clock is also reset on the falling edge. The PWM frequency, duty cycle and time active can be calculated because the TC clock frequency, and therefore the duration of each count, is known.

I have tested this using all 5 available TIOAx channels individually and they all seem to work. I have not yet tested multiple channels running at the same time. The sketch also has some issues when the PWM signal is noisy, occasionally giving very incorrect values.

TL;DR The timer counter calculates PWM frequency and duty cycle while using almost no CPU time (i.e., it is non-blocking)

Code:
/*
 * PWM Capture using Arduino Due Timer Counters
 *
 * Available channels:
 *   TC    Chan   NVIC irq   Handler       PMC ID   Arduino Pin
 *   TC0   0      TC0_IRQn   TC0_Handler   ID_TC0   D2     (TIOA0)
 *   TC0   1      TC1_IRQn   TC1_Handler   ID_TC1   D61/A7 (TIOA1)
 *   TC2   0      TC6_IRQn   TC6_Handler   ID_TC6   D5     (TIOA6)
 *   TC2   1      TC7_IRQn   TC7_Handler   ID_TC7   D3     (TIOA7)
 *   TC2   2      TC8_IRQn   TC8_Handler   ID_TC8   D11    (TIOA8)
 *
 * Change the following defines to use different channels as input.
 *
 */

#define CAPTURE_TC TC0
#define CAPTURE_CHANNEL 0
#define CAPTURE_IRQn TC0_IRQn
#define CAPTURE_Handler TC0_Handler
#define CAPTURE_ID ID_TC0
#define CAPTURE_PIN 2
#define CAPTURE_CLOCK_SELECTION TC_CMR_TCCLKS_TIMER_CLOCK3

// clock divisors corresponding to CAPTURE_CLOCK_SELECTION
static const uint32_t divisors[5] = { 2, 8, 32, 128, 0};


volatile uint32_t captured_pulses = 0;
volatile uint32_t captured_ra = 0;
volatile uint32_t captured_rb = 0;
uint32_t frequency, duty_cycle, active_time;


// timer interrupt handle
void CAPTURE_Handler() {
  if ((TC_GetStatus(CAPTURE_TC, CAPTURE_CHANNEL) & TC_SR_LDRBS) == TC_SR_LDRBS) {
    captured_pulses++;
    captured_ra = CAPTURE_TC->TC_CHANNEL[CAPTURE_CHANNEL].TC_RA;
    captured_rb = CAPTURE_TC->TC_CHANNEL[CAPTURE_CHANNEL].TC_RB;
  }
}


void setup() {
  Serial.begin(57600);
  Serial.print("Initializing...");
  
  // configure the PIO pin as peripheral
  const PinDescription *config = &g_APinDescription[CAPTURE_PIN];
PIO_Configure(
 config->pPort,
 config->ulPinType,
 config->ulPin,
 config->ulPinConfiguration
);
    
  // enable timer peripheral clock
  pmc_enable_periph_clk(CAPTURE_ID);
  
  // configure the timer
  TC_Configure(CAPTURE_TC, CAPTURE_CHANNEL,
    CAPTURE_CLOCK_SELECTION /* Clock Selection */
    | TC_CMR_LDRA_RISING /* RA Loading: rising edge of TIOA */
    | TC_CMR_LDRB_FALLING /* RB Loading: falling edge of TIOA */
    | TC_CMR_ABETRG /* External Trigger: TIOA */
    | TC_CMR_ETRGEDG_FALLING /* External Trigger Edge: Falling edge */
  );
  
  // configure TC interrupts
  NVIC_DisableIRQ(CAPTURE_IRQn);
  NVIC_ClearPendingIRQ(CAPTURE_IRQn);
  NVIC_SetPriority(CAPTURE_IRQn, 0);
  NVIC_EnableIRQ(CAPTURE_IRQn);
  
  // enable interrupts
  CAPTURE_TC->TC_CHANNEL[CAPTURE_CHANNEL].TC_IER = TC_IER_LDRBS;
  
  // start timer counter
  CAPTURE_TC->TC_CHANNEL[CAPTURE_CHANNEL].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;
  
  Serial.println("ready!");
  
}


void loop() {
  // measurement is interrupt based so delay doesn't really block it
  delay(1000);
  
  Serial.print("Captured "); Serial.print(captured_pulses);
  Serial.println(" pulses from TC since last read");
  captured_pulses = 0;
  
  // frequency in Hz
  frequency
    = (F_CPU / divisors[CAPTURE_CLOCK_SELECTION]) / captured_rb;
  
  // duty cycle in percent
  duty_cycle
    = (captured_rb - captured_ra) * 100 / captured_rb;
  
  // time active in microseconds
  active_time
    = ((captured_rb - captured_ra) * 1000) /
      ((F_CPU / divisors[CAPTURE_CLOCK_SELECTION]) / 1000);
  
  Serial.print("Captured wave frequency = "); Serial.print(frequency);
  Serial.print(" Hz, Duty cycle = "); Serial.print(duty_cycle);
  Serial.print(" %, Pulse width = "); Serial.print(active_time);
  Serial.println(" us");
  
}

output:
Code:
Initializing...ready!
Captured 52 pulses from TC since last read
Captured wave frequency = 52 Hz, Duty cycle = 6 %, Pulse width = 1292 us
Captured 53 pulses from TC since last read
Captured wave frequency = 52 Hz, Duty cycle = 6 %, Pulse width = 1292 us
Captured 53 pulses from TC since last read
Captured wave frequency = 52 Hz, Duty cycle = 6 %, Pulse width = 1290 us
Captured 52 pulses from TC since last read
Captured wave frequency = 52 Hz, Duty cycle = 6 %, Pulse width = 1292 us
11  Products / Arduino Due / Re: PWM Duty Cycle Capture using Timer Counter on: May 24, 2013, 06:02:47 am
I haven't figured it out yet, but I'm still working on it. There is example code included in the Atmel Software Framework (tc_capture_waveform_example.c) that should do exactly what I'm attempting to do but it seems that a lot of the driver functions they use aren't included in the Arduino IDE.

I will post some code as soon as I get this sorted out.
12  Products / Arduino Due / [SOLVED] Non-blocking PWM duty cycle capture using Timer Counter on: May 22, 2013, 07:39:12 am
May 25, 2013 - See my later post for working code

I am attempting to use one of the Due's timer/counters to measure the duty cycle of a PWM signal from an RC receiver. Using the TC's external trigger I want the counter to reset at every rising edge (start of duty cycle) and then record the timer value at the falling edge (end of duty cycle) to one of the TC's registers. What I was expecting to see as output was the number of counts that the signal was high, however all I get is 0.

I have also attached an interrupt such that every time the register is loaded by the TC (i.e., every falling edge) it is read and a variable that counts the number of pulses is incremented. The number of pulses increases as expected. See the code and example output below.

Does anyone know why the counter doesn't appear to be actually counting?

RX_example.ino
Code:
#define TC_WPMR_WPKEY_VALUE TC_WPMR_WPKEY((uint32_t)0x54494D)

volatile uint32_t duty_value = 0;
volatile uint32_t pulses_captured = 0;

long start_time, previous_time;

// timer interrupt handle
void TC0_Handler() {
  TC_GetStatus(TC0, 0);
  
  pulses_captured++;
  duty_value = TC0->TC_CHANNEL[0].TC_RA;
  
}



void setup() {
  Serial.begin(57600);
  Serial.print("Initializing...");
  
  // enable timer clock
  pmc_enable_periph_clk(ID_TC0);
  
  // configure the PIO pin as peripheral
  PIO_Configure(
    g_APinDescription[2].pPort,
    g_APinDescription[2].ulPinType,
    g_APinDescription[2].ulPin,
    g_APinDescription[2].ulPinConfiguration);
  
  // disable TC register write protection
  TC0->TC_WPMR = TC_WPMR_WPKEY_VALUE;
  
  // configure the timer
  TC_Configure(TC0, 0,
    TC_CMR_TCCLKS_TIMER_CLOCK4
    | TC_CMR_ETRGEDG_RISING
    | TC_CMR_ABETRG
    | TC_CMR_LDRA_FALLING
  );

  // start counter
  TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;
  
  // enable interrupts on loading of Register A
  TC0->TC_CHANNEL[0].TC_IER=TC_IER_LDRAS;
  TC0->TC_CHANNEL[0].TC_IDR=~TC_IER_LDRAS;
  
  // enable the NVIC (Nested Vector Interrupt Controller)
  NVIC_EnableIRQ(TC0_IRQn);
  
  Serial.println("ready!");
  
  previous_time = micros();
}


void loop() {
  start_time = micros();
  
  if(start_time - previous_time > 50000) {
    Serial.print(pulses_captured); Serial.print(",");
    Serial.println(duty_value);
    
    previous_time = start_time;
  }
}

output.txt
Code:
Initializing...ready!
5,0
11,0
17,0
23,0
27,0
33,0
39,0
45,0
50,0
55,0
61,0
67,0
73,0
77,0
83,0
89,0
96,0
102,0
107,0
113,0
119,0
125,0
129,0
136,0
143,0
150,0
155,0
...
13  Using Arduino / Microcontrollers / Re: Anyone dare to port arduino for STM32F4? on: April 18, 2013, 11:40:39 am
I am not aware of any ports. The stm32f4 is ARM based just like the Arduino Due, but from a different manufacturer (i.e., not ATMEL). Therefore the SAM3X libraries in Arduino 1.5.x would need to be replaced, which would require a lot of work.

On the other hand, I have been having a lot of luck programming a stm32f4 discovery board using the GNU ARM toolchain (available at https://launchpad.net/gcc-arm-embedded) and the open source stlink software (at https://github.com/texane/stlink) to compile and flash my programs. While this is a bit more work, ST provides a lot of libraries and examples that show how to interact with the board's peripherals (e.g. i2c, spi, etc.). I have this all set up under Mac OS 10.7 but from what I can gather it is much easier to do under windows.
14  Using Arduino / Sensors / Re: Exceeding the maximum output data rate of the HMC5883L on: January 20, 2013, 12:39:07 am
Those are the kind of issues I had imagined, although I am mainly concerned about heating.

The quality of the measurements does decrease with the higher sampling frequencies. I don't really know how to measure the accuracy of the device, but I did record a few seconds of data while it was stationary. There is a noticeable increase in noise when sampling at 200 Hz, but not enough that it couldn't be filtered out. See for yourself:


P.S. Thanks for catching that potential infinite loop, I don't know how I missed that

[EDIT] Fixed the image link
15  Using Arduino / Sensors / Exceeding the maximum output data rate of the HMC5883L on: January 19, 2013, 04:10:18 pm
In the data sheet of the HMC5883L 3-axis compass the maximum output data rate is listed as 160 Hz when using single-measurement mode (interrupt driven sampling). I've written a simple arduino sketch using interrupts to only grab data when the device data ready pin has changed status, indicating the samples are ready. However, I've found that with my code I am able to sample at up to ~240 Hz. Will sampling at this rate for long durations damage the sensor? Why else would the listed maximum rate be so much lower than what I can achieve?

Here is my code where I've limited the sampling to 200 Hz :

Code:
#include <Wire.h>

#define MAG_ADDRESS ((char) 0x1E)
uint8_t mag_buffer[6];
int16_t mag_raw[3];

long start_time = 0;
long previous_time = 0;
long loop_duration = 0;

volatile boolean isDataReady = false;

void setup() {
  Serial.begin(115200);
  Serial.println("Initializing...");
 
  Wire.begin();

  configMag();
 
  attachInterrupt(13, magDataReady, RISING);
 
  previous_time = micros();
}


//interrupt function when mag DRDY pin is brought LOW
void magDataReady() {
  isDataReady = true;
}


void loop() {

  start_time = micros();
  loop_duration = start_time - previous_time;
 
  if(loop_duration >= 5000) {
    if(isDataReady) {
      isDataReady = false;
      readMag();
 
//      Serial.print(mag_raw[0], DEC); Serial.print(",");
//      Serial.print(mag_raw[1], DEC); Serial.print(",");
//      Serial.print(mag_raw[2], DEC); Serial.println();
      previous_time = start_time;
   
    }
    else {
      Serial.println("Missed one");
    }
  }
 
}


void configMag() {
  uint8_t mag_name;
 
  // make sure that the device is connected
  Wire.beginTransmission(MAG_ADDRESS);
  Wire.write((byte) 0x0A); // Identification Register A
  Wire.endTransmission();
 
  Wire.beginTransmission(MAG_ADDRESS);
  Wire.requestFrom(MAG_ADDRESS, 1);
  mag_name = Wire.read();
  Wire.endTransmission();
 
  if(mag_name != 0x48) {
    Serial.println("HMC5883L not found!");
    Serial.print(mag_name, HEX); Serial.println(" found, should be 0x48");
    delay(1000);
  }
 
  // Register 0x00: CONFIG_A
  // normal measurement mode (0x00) and 75 Hz ODR (0x18)
  Wire.beginTransmission(MAG_ADDRESS);
  Wire.write((byte) 0x00);
  Wire.write((byte) 0x18);
  Wire.endTransmission();
  delay(5);
 
  // Register 0x01: CONFIG_B
  // default range of +/- 130 uT (0x20)
  Wire.beginTransmission(MAG_ADDRESS);
  Wire.write((byte) 0x01);
  Wire.write((byte) 0x20);
  Wire.endTransmission();
  delay(5);
 
  // Register 0x02: MODE
  // continuous measurement mode at configured ODR (0x00)
  // possible to achieve 160 Hz by using single measurement mode (0x01) and DRDY
  Wire.beginTransmission(MAG_ADDRESS);
  Wire.write((byte) 0x02);
  Wire.write((byte) 0x01);
  Wire.endTransmission();
 
  delay(200);
 
}


// read 6 bytes (x,y,z magnetic field measurements) from the magnetometer
void readMag() {
 
  // multibyte burst read of data registers (from 0x03 to 0x08)
  Wire.beginTransmission(MAG_ADDRESS);
  Wire.write((byte) 0x03); // the address of the first data byte
  Wire.endTransmission();
 
  Wire.beginTransmission(MAG_ADDRESS);
  Wire.requestFrom(MAG_ADDRESS, 6);  // Request 6 bytes
  int i = 0;
  while(Wire.available())
  {
    mag_buffer[i] = Wire.read();  // Read one byte
    i++;
  }
  Wire.read();
  Wire.endTransmission();
 
  // combine the raw data into full integers (HMC588L sends MSB first)
  //           ________ MSB _______   _____ LSB ____
  mag_raw[0] = (mag_buffer[0] << 8) | mag_buffer[1];
  mag_raw[1] = (mag_buffer[2] << 8) | mag_buffer[3];
  mag_raw[2] = (mag_buffer[4] << 8) | mag_buffer[5];
 
  // put the device back into single measurement mode
  Wire.beginTransmission(MAG_ADDRESS);
  Wire.write((byte) 0x02);
  Wire.write((byte) 0x01);
  Wire.endTransmission();
 
}
Pages: [1] 2