I tried to compile a program currently running on the UNO board and run it on the DUE board. However, the time returned by the pulseIn instruction is not the same on a DUE board, and the error is large, which is a problem.What is required for the pulseIn instruction to work properly on a DUE board?
Arduino IDE windows 1.8.12
--code--
int pin = 2;
unsigned long duration;
void setup() {
Serial.begin(9600);
pinMode(pin, INPUT);
}
void loop() {
duration = pulseIn(pin, HIGH);
Serial.println(duration);
}
-- result 500Hz pulse input
UNO
986
985
985
985
985
985
985
985
985
986
985
986
986
986
DUE
929
953
962
962
897
929
897
963
963
897
929
897
963
963
m-miyake:
What is required for the pulseIn instruction to work properly on a DUE board?
A clean 3.3V signal I guess.
westfw
February 26, 2020, 6:23am
3
Probably a version that doesn't try to guess instruction timing on a CPU with non-deterministic flash memory timing
pulseln() is not designed for the DUE. You need a timer set in Capture Mode.
An example sketch to capture frequency and duty cycle of an input pulse provided by the board itself:
/*************************************************************************************************/
/* a jumper needs to be installed between pin 2 (TIOA0) and pin A7 (TC0 channel 1 TIOA pin) */
/*************************************************************************************************/
//#define Serial SerialUSB
volatile uint32_t CaptureCountA, CaptureCountB, Period, Duty;
volatile boolean CaptureFlag;
void setup() {
Serial.begin(250000);
/************* Timer Counter 0 Channel 0 to generate PWM pulses thru TIOA0 ************/
PMC->PMC_PCER0 |= PMC_PCER0_PID27; // Timer Counter 0 channel 0 IS TC0
PIOB->PIO_PDR |= PIO_PDR_P25 | PIO_PDR_P27;
PIOB->PIO_ABSR |= PIO_ABSR_P25 | PIO_ABSR_P27;
TC0->TC_CHANNEL[0].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 // MCK/2, clk on rising edge
| TC_CMR_WAVE // Waveform mode
| TC_CMR_WAVSEL_UP_RC // UP mode with automatic trigger on RC Compare
| TC_CMR_ACPA_CLEAR // Clear TIOA0 on RA compare match
| TC_CMR_ACPC_SET; // Set TIOA0 on RC compare match
TC0->TC_CHANNEL[0].TC_RC = 42000; // Frequency of PWM pulses = MCK/2/TC_RC
TC0->TC_CHANNEL[0].TC_RA = 21000; // Duty cycle of PWM pulses = (TC_RA/TC_RC) * 100 %
TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger and enable
/************* Timer Counter 0 Channel 1 to capture PWM pulses thru TIOA1 ************/
PMC->PMC_PCER0 |= PMC_PCER0_PID28; // Timer Counter 0 channel 1 IS TC1
TC0->TC_CHANNEL[1].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 // capture mode, MCK/2, clk on rising edge
| TC_CMR_ABETRG // TIOA is used as the external trigger
| TC_CMR_LDRA_RISING // load RA on rising edge of trigger input
| TC_CMR_LDRB_FALLING; // load RB on falling edge of trigger input
TC0->TC_CHANNEL[1].TC_IER |= TC_IER_LDRAS | TC_IER_LDRBS; // Trigger interruption on Load RA and load RB
TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger and enable
NVIC_DisableIRQ(TC1_IRQn);
NVIC_ClearPendingIRQ(TC1_IRQn);
NVIC_SetPriority(TC1_IRQn, 0); // Give TC1 interrupt the highest urgency
NVIC_SetPriority(SysTick_IRQn, 15); // SysTick interrupt will not interrupt TC1 interrupt
NVIC_SetPriority(UART_IRQn, 14); // UART interrupt will not interrupt TC1 interrupt
NVIC_EnableIRQ(TC1_IRQn); // Enable TC1 interrupts
}
void loop() {
const uint32_t _F_CPU = F_CPU / 2;
static uint32_t counter;
float Frequency;
float _Duty;
if (counter++ > 100000) {
if ( CaptureFlag == true) {
Frequency = _F_CPU / Period ; // (Mck/2 is TC1 clock) F in Hz
_Duty = (Duty * 100.00) / Period;
printf(" F = %d Hz , Duty = %d per cent\n", (uint32_t)Frequency, (uint32_t)_Duty);
counter = 0;
CaptureFlag = false;
}
}
}
void TC1_Handler() {
static uint32_t _CaptureCountA;
uint32_t status = TC0->TC_CHANNEL[1].TC_SR; // Read and Clear status register
//if (status & TC_SR_LOVRS) abort(); // We are loosing some edges
if (status & TC_SR_LDRAS) { // If ISR is triggered by LDRAS then ....
CaptureCountA = (uint32_t) TC0->TC_CHANNEL[1].TC_RA; // get data from capture register A for TC0 channel 1
Period = CaptureCountA - _CaptureCountA;
_CaptureCountA = CaptureCountA;
}
else { /*if ((status & TC_SR_LDRBS) == TC_SR_LDRBS)*/ // If ISR is triggered by LDRBS then ....
CaptureCountB = (uint32_t) TC0->TC_CHANNEL[1].TC_RB; // get data from caputre register B for TC0 channel 1
Duty = CaptureCountB - _CaptureCountA;
CaptureFlag = true; // set flag indicating a new capture value is present
}
}
westfw
February 26, 2020, 7:15am
5
Do you know which timers on Due are available?
I was thinking that the SAM3X might get away with the same micros()-based pulsein() that I did for the Adafruit SAMD51 boards
Thank you everyone for advice.
I understand that the pulseIn instruction cannot be used with the DUE board.
The following method was used.
--- code ---
volatile unsigned long duration=0;
volatile unsigned long prevMicros=0;
float freq;
void setup()
{
Serial.begin(9600);
attachInterrupt(2, periodIrq, RISING);
}
void loop()
{
freq = 1000000/duration;
Serial.println(freq);
delay(200);
}
void periodIrq() // interrupt handler
{
unsigned long currentMicros = micros();
duration = currentMicros - prevMicros;
prevMicros = currentMicros;
}
The loop access to the variable duration has to be atomic.
See Gammon Forum : Electronics : Microprocessors : Interrupts
westfw
February 27, 2020, 6:55am
8
The loop access to the variable duration has to be atomic.
Isn't it always? Remember this is a 32bit chip...
westfw:
Isn't it always? Remember this is a 32bit chip...
Probably. I forgot about the 32 bit architecture.