Hi guys, I am trying to simulate the function of an incremental encoder using the DUE. The idea is for it to work in the following way:-
-
Use a Timer Interrupt to change the duty cycle of a PWM signal (high 75% & low 25%)
-
Read in a supply voltage
-
Based on the level of the supply voltage, change the frequency of the interrupt timer.
Lets take an example:
IF at max voltage, max PWM freq is 100,000 Hz, max Timer frequency is 1000 Hz (meaning duty cycle of PWM changes from 75% to 25% with a frequency of 1000 Hz)
At half the voltage, Timer Interrupt frequency drops to half. The PWM frequency remains the same, however, the frequency of the change of the duty cycle of the PWM drops to half.
Here is the code I am using for it (Ive missed out the variable initialisations in order to meet the character limit):
#define motorFrequency1 1000
void setup() {
Serial.begin(115200);
pinMode(pinPWM,OUTPUT);
pinMode(pinDuty, INPUT);
/************************************************************************************************
* Output PWM thru TIOA0
************************************************************************************************/
PMC->PMC_PCER0 |= PMC_PCER0_PID27; // Timer Counter 0 channel 0 IS TC0, TCO power ON
PMC->PMC_PCER0 |= PMC_PCER0_PID12; // PIOB power ON, page 38
PIOB->PIO_PDR |= PIO_PDR_P25;
PIOB->PIO_ABSR |= PIO_ABSR_P25; // PB25 is driven by the TC, peripheral type B, page 858
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 = 420; //<********************* Frequency = (Mck/2)/TC_RC
TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC0 counter and enable
/************************************************************************************************
* Generate pulses thru TIOA1 and use it as Interrupt
************************************************************************************************/
pmc_set_writeprotect(false);
pmc_enable_periph_clk((uint32_t)ID_TC1);
TC0->TC_CHANNEL[1].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[1].TC_IER = TC_IER_CPCS // Interrupt on RC compare match
| TC_IER_CPAS; // Interrupt on RA compare match
NVIC_EnableIRQ(TC1_IRQn); // Interrupt enable
TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC0 counter and enable
}
/************************************************************************************************
* Interrupt Handler for Timer Counter 0 Channel 1 (TC1)
************************************************************************************************/
void TC1_Handler()
{
TC_GetStatus(TC0, 1);
if (counter == 0)
{
TC0->TC_CHANNEL[0].TC_RA = 336; //<******************** Duty cycle = (TC_RA/TC_RC) * 100 = 75 %
counter = 1;
}
else
{
TC0->TC_CHANNEL[0].TC_RA = 110; //<******************** Duty cycle = (TC_RA/TC_RC) * 100 = 25 %
counter = 0;
}
}
/************************************************************************************************
* Main Loop
************************************************************************************************/
void loop() {
/**********************************************************************************************
* Loop conditions for Standard PWM voltage output and reading that voltage filtered to AC back in at A0
**********************************************************************************************/
analogWrite(pinPWM, 255);
//take a number of analog samples and add them up
//continues to iterate until two successive values greater than 200 are seen
//or if the time elapsed reaches a greater value than the interval
while (sample_count < NUM_SAMPLES) {
//introduce interval comparison
unsigned long currentMillis = millis();
check = 0;
check2 = 0;
;
check = analogRead(pinVoltage);
if (check > 200)
{
//Serial.print("1st measurement: ");
//Serial.println(check);
check2 = analogRead(pinVoltage);
if (check2 > 200){
//Serial.print("2nd measurement: ");
//Serial.println(check2);
sample_count = 2;
}
}
//Check if the time elapsed is greater than the interval (2ms)
//If not, continue while loop, if yes, end while loop
else if (currentMillis - previousMillis < interval)
{
previousMillis = currentMillis;
sample_count = 2;
}
sum = check + check2;
delay(10);
}
voltage = ((float)sum / 2 * 3.3)/1024.0;
originalVoltage = voltage * 4;
sample_count = 0;
sum = 0;
/*******************************
* 10000 Hz == 4200 Counts @ 13V
* Gradient = 10000/13 = 769.23
* frequency = 769.23 * Vin
* freq = 42000000/frequency
*******************************/
int freq = 42000000/(originalVoltage * (motorFrequency1/13.2));
TC0->TC_CHANNEL[1].TC_RC = freq;
}
I am using the digital output to provide the supply voltage. Ofcourse with lower duty cycle this DC signal is first filtered through Low pass.
The voltage is read back in and set inside the main loop. Depending on the value of the voltage, in the final command of the while loop, the frequency for the interrupt is set (code pasted again below):
int freq = 42000000/(originalVoltage * (motorFrequency1/13.2));
TC0->TC_CHANNEL[1].TC_RC = freq;
Then, using this frequency, inside the interrupt handler, the duty cycle of the original PWM is altered (code pasted again below):
void TC1_Handler()
{
TC_GetStatus(TC0, 1);
if (counter == 0)
{
TC0->TC_CHANNEL[0].TC_RA = 336; //<******************** Duty cycle = (TC_RA/TC_RC) * 100 = 75 %
counter = 1;
}
else
{
TC0->TC_CHANNEL[0].TC_RA = 110; //<******************** Duty cycle = (TC_RA/TC_RC) * 100 = 25 %
counter = 0;
}
}
The Issues I am facing with this are:
at high Interrupt frequencies, the DUE just keeps going around the while loop for the first few minutes, and then once the interrupt happens, it get stuck there and doesnt iterate through the loop again.
At low frequencies (about 1Hz - 50 Hz) for the interrupt, everything seems to work fine.
Can somebody please explain to me why I am seeing such a behaviour? What can I do to make my program work at higher frequencies as well?
Really appreciate the help. Please let me know if anything is unclear about the project and if I can explain something better.