I am new to using an arduino, or any development board for that matter. But the current project I am working on is to read the time difference between when rising edge 1 happens and rising edge 2 happens. And between when rising edge 1 happens and rising edge 3 happens.
The project is reading the sensor angle alignment of a sensored bldc motor. These are supposed to be 120* apart, but they can vary a bit. I would like to be able to get measurements to the tenth of a degree. The frequency can vary from 1 - 1khz. I am attaching a pic of the output from my scope.
So that is really cool, and I can use that for the 3 different phases, but I don't see how to measure time from one sensor to another sensor. Mostly that tutorial just interacts with device, then does multiple devices at the same time.
So I am going to post kind of what I want it to do. The first part of the loop probably works, as I found something someone posted and slightly modified it. But I don't know how to make the second part work. Also I want to use port registers instead of the pin names.
DDRB &= 0x07; // Configure pins 8, 9, and 10 as inputs (RPM Sensor)
PORTB = 0x00;
int lastValueCS = 0
int lastValueCE = 0
valueCS = digitalRead(10) // Sensor C
valueCE = digitalRead(5) // Phase C
void loop() {
if(valueCS == HIGH && lastValueCS ==LOW){
risingedge = micros();
PhaseC++;
}
else
lastValueCS = valueCS;
if(PhaseC > 20){
if(valueCE == HIGH && lastValueCE ==LOW){
Stop PhaseC++;
Store elapsed time as variable;
}
else
lastValueCE = valueCE;
}
}
Once I get this working, I want to put it into the below code which does work. I want to count the time between each rpm sensor and the corresponding back emf. I am currently having digital pins 3,4,5 write high and low, I am not sure if that is actually needed, or if I can use that to make my readings on. Just as an fyi, I don't know C or C+. But I have been working in IT as a sys admin and network engineer for the last 10 years. What I can do is google to get something close to what I want then try to modify it. The below code I found in the public domain and modified it for my needs. I am still commenting parts out, but the pwm sections have been removed.
// Test for reading zero cross
byte bldc_step = 0, motor_speed;
unsigned int i;
void setup() {
DDRD |= 0x38; // Configure pins 3, 4, and 5 as outputs (BEMF Detection)
PORTD = 0x00;
DDRB &= 0x07; // Configure pins 8, 9, and 10 as inputs (RPM Sensor)
PORTB = 0x00;
Serial.begin (9600);
}
// Analog comparator ISR
ISR (ANALOG_COMP_vect) {
// BEMF debounce
for(i = 0; i < 200; i++) {
if(bldc_step & 1){
if(!(ACSR & 0x20)) i -= 1;
}
else {
if((ACSR & 0x20)) i -= 1;
}
}
bldc_move();
bldc_step++;
bldc_step %= 6;
}
void bldc_move(){ // BLDC motor commutation function
switch(bldc_step){
case 0:
AH_BL();
BEMF_C_RISING();
break;
case 1:
/*AH_CL();
BEMF_B_FALLING();*/
break;
case 2:
BH_CL();
BEMF_A_RISING();
break;
case 3:
/*BH_AL();
BEMF_C_FALLING();*/
break;
case 4:
CH_AL();
BEMF_B_RISING();
break;
case 5:
/*CH_BL();
BEMF_A_FALLING();*/
break;
}
}
void loop() {
ACSR |= 0x08; // Enable analog comparator interrupt
}
void BEMF_A_RISING(){
ADCSRA = (0 << ADEN); // Disable the ADC module
ADCSRB = (1 << ACME);
ADMUX = 1; // Select analog channel 1 as comparator negative input
ACSR |= 0x03; // Set interrupt on rising edge
}
/*void BEMF_A_FALLING(){
ADCSRA = (0 << ADEN); // Disable the ADC module
ADCSRB = (1 << ACME);
ADMUX = 1; // Select analog channel 1 as comparator negative input
ACSR &= ~0x01; // Set interrupt on falling edge
}*/
void BEMF_B_RISING(){
ADCSRA = (0 << ADEN); // Disable the ADC module
ADCSRB = (1 << ACME);
ADMUX = 2; // Select analog channel 2 as comparator negative input
ACSR |= 0x03;
}
/*void BEMF_B_FALLING(){
ADCSRA = (0 << ADEN); // Disable the ADC module
ADCSRB = (1 << ACME);
ADMUX = 2; // Select analog channel 2 as comparator negative input
ACSR &= ~0x01;
}*/
void BEMF_C_RISING(){
ADCSRA = (0 << ADEN); // Disable the ADC module
ADCSRB = (1 << ACME);
ADMUX = 3; // Select analog channel 3 as comparator negative input
ACSR |= 0x03;
}
/*void BEMF_C_FALLING(){
ADCSRA = (0 << ADEN); // Disable the ADC module
ADCSRB = (1 << ACME);
ADMUX = 3; // Select analog channel 3 as comparator negative input
ACSR &= ~0x01;
}*/
void AH_BL(){
// C Rising
PORTD &= ~0x18; // D3 and D4 LOW
PORTD |= 0x20; // D5 HIGH
}
/*void AH_CL(){
// B Falling
PORTD &= ~0x18; // D3 and D4 LOW
PORTD |= 0x20; // D5 HIGH
}*/
void BH_CL(){
// A Rising
PORTD &= ~0x28; //D3 and D5 LOW
PORTD |= 0x10; // D4 HIGH
}
/*void BH_AL(){
// C Falling
PORTD &= ~0x28; //D3 and D5 LOW
PORTD |= 0x10; //D4 HIGH
}*/
void CH_AL(){
// B Rising
PORTD &= ~0x30; //D4 and D5 LOW
PORTD |= 0x08; //D3 HIGH
}
/*void CH_BL(){
// A Falling
PORTD &= ~0x30; //D4 and D5 LOW
PORTD |= 0x08; //D3 HIGH
}*/
Yes... I think to do that I have to use input polling?
Also, I just picked up an Uno Rev 4. So maybe I can move this project to that.
But regardless, I get how to start the counting, but I don't understand how to stop that count once the rising edge of the second waveform is detected. So input on pin 10, detect rising edge. Output on pin 5 detect rising edge. Measure time between the two events. Do this in a way that gets the best resolution, then average it over maybe 50-100 samples. Then I guess create a class and have the arduino do this for all three phases at the same time.
Ok, quick search on the mega2560 and it says it has 4 16 bit timers. So that should work. But regardless, I'll start with just the one on the uno. Thank you again, I think you have given me enough to work off of.
are you measuring the time between rising edges on one pin or 2?
wouldn't that be the time of a cycle, half of which is the period between turning each phase on/off and 1/3 the time is the phase between each signal
knowing these times and synchronizing to a signal allows you to determine the time relative to the synchronizing pulse to toggle each of the 3 signals
does the following code make any sense to you?
the timer conditions, ledIdx and state determine exactly what to do at the start of each timer expiration (i.e. no need to test any condition or do any calculations) and then determines the parameters for the next timer expiration
const byte PinBut = A1;
const byte PinLeds [] = { 13, 12, 11 };
const int Nled = sizeof (PinLeds);
byte butLst;
int idx;
int stp;
byte state;
unsigned long msecPeriod;
unsigned long msecHalf;
unsigned long msecPhase;
unsigned long msec1;
unsigned long msec0;
void
loop (void)
{
unsigned msec = millis ();
if (msecPeriod && msec - msec0 >= msecPeriod) {
digitalWrite (PinLeds [idx], state);
Serial.print (idx);
Serial.print (" ");
Serial.println (state);
switch (stp++) {
case 1: // pre raise 1
msecPeriod = msecPhase;
state = HIGH;
idx = 1;
break;
case 2: // pre lower 0
msecPeriod = msecHalf;
state = LOW;
idx = 0;
break;
case 3: // pre raise 2
msecPeriod = 2 * msecPhase;
state = HIGH;
idx = 2;
break;
case 4: // pre lower 1
msecPeriod = msecPhase + msecHalf;
state = LOW;
idx = 1;
break;
case 5: // pre raise 0
msecPeriod = 3 * msecPhase;
state = HIGH;
idx = 0;
break;
case 6: // pre lower 2
msec0 = msec;
stp = 0;
msecPeriod = 2 * msecPhase - msecHalf;
state = LOW;
idx = 2;
break;
}
}
byte but = digitalRead (PinBut);
if (butLst != but) {
if (LOW == but && msec - msec0 > 20) {
msec1 = msec;
digitalWrite (PinLeds [0], HIGH); // raise 1
if (msec0) {
msecHalf = (msec1 - msec0) / 2;
msecPhase = (msec1 - msec0) / 3;
stp = 0;
idx = 2; // pre lower 2
state = LOW;
msecPeriod = 2 * msecPhase - msecHalf;
}
msec0 = msec1;
}
butLst = but;
}
}
void
setup (void)
{
Serial.begin (9600);
pinMode (PinBut, INPUT_PULLUP);
butLst = digitalRead (PinBut);
for (int n = 0; n < Nled; n++) {
pinMode (PinLeds [n], OUTPUT);
digitalWrite (PinLeds [n], HIGH);
}
}
this was tested by timing button presses and observing an led sequence..
Yes to the 1khz. A 5.0t 540 motor will run somewhere between 50-60k rpm. Most motors that I will be using will top out below 30k rpm though. But that is still 560ns if I want to be able to measure to tenths of a degree.
1% would be 1.2deg. what would be the effect of a 1% error be, torque?
i assume the freq determines the speed. 1% error would be 10 usec at 1kHz.
some algorithm will have some error on a particular processor. presumably that error can be reduced by using a faster processor. a TI dsp iused in an optical amplifier was targeted for motor control. i always assumes was overkill but that the motor control engineers probably weren't the most proficient programmers.
i guess you'll figure out if the error using an Arduino is acceptable, and if not find a faster processor. unlike risc processors, dsps are good at executing loops
I'll just say that rc racers can be perfectionists and they will pay more for motors that the hall sensors are spaced exactly 120* apart. There are currently 3 devices on the market right now that can measure the "can timing" and give how far off the sensors are. One will measure to the tenths (it uses an xmega256 mcu), one measures to whole degrees, and the other I have not bought yet. These devices are expensive, the one that measures to tenths cost $450. But they also drive the motor as well, and I am nowhere near that level in my electronics hobby yet.
And maybe the 328p won't be good enough. But I would like to stay with 5v, and I was really hoping the ra4m1 mcu on the uno 4 would be up to the task.
Ok, so after some digging on the web. The mega2560 does have 4 16 bit timers with an input capture. However, only timer 4 and 5 input captures are broke out on the Arduino mega 2560. Timer 3 and timer 1 do not have mappings. But, timer 1 can be used through the analog comparator. Which brings me to my next question.
Can the analog comparator use interrupts and timer 1 at the same time as the icp1? My code is already using timer 1 via the analog comparator.
Detecting the zero cross for the 3 phases on the bldc motor. So really it is already getting 3 timestamps that I want. I just have to access that. Then compare to the hall effect sensor readings.
So maybe I need to get this working just using micros() to begin with. I think after I get a good proof of concept using the micros, I'll then design a pcb with a mega 2560 where I can use the pins I want and get 3 input capture pins and multiple hardware interrupt pins and still use time 1 for the bemf. But it looks like I will need to make my own board for that.
What I am thinking for using micros is to use pin change interrupts. I think I can put an hall effect sensor signal and the corresponding output from the bemf on each of the ports. Then use pin change for both rising and falling with the hall effect sensor (I have use for both), and just use the rising edge for the bemf. Am I correct that I can initiate a read micros() from an ISR and store to a variable?