Arduino, write the output of comparator to view on scope - Page 1
Forum section: electronics / beginners

author: trilerian
author_rank: Regular Contributor
timestamp: June 26, 2023, 08:33:47 pm
content: I would like to view the output from using the analog comparator built into the arduino uno on my oscilloscope. But I don't know how to do this. I am researching online, but I am still really new to this. The objective is to use the comparator as a zero cross for bldc motor and then compare that to the hall sensor rising edge to determine the advanced can timing on the motor. Just comparing the hall sensors to the back emf is easy, but slightly off with tools I have. So I think I need the actual zero cross here. I am using the code from eletricnoob for zero cross. I just have it comparing to one phase right now on pinA0. But like I said I would like to view the results on my scope so I can compare it to the hall sensor on the scope. Then measure the difference between the two.Here is the code:I tried to add: pinMode(13,OUTPUT); to the setup.and digitalWrite(13,HIGH);and digitalWrite(13,LOW);where the code references "do what you want here".But I get nothing on my scope.void setup() {ACSR |= B00010000; // Clear flag comparator interrupt (ACI bit to 1)ACSR &= B11011111; // Set ACBG, to be equal to "0"ADCSRA = (0 && ADEN); // Disable the ADC module becauseADCSRB = (1 && ACME); // Enable the MUX selector for negative input of comparatorADMUX = 0; // Select A0 as comparator negative inputACSR |= B00000011; // Set interrupt on rising edge*/}void loop() {//Do what you want to do in the loop...}// Interrumption vector for the Analog comparatorISR (ANALOG_COMP_vect) {if(ACSR & B00100010) //If we are into falling edge{if(!(ACSR & B00100000)) { //If ACO is 0 (we have that ! for inverse)//A change from HIGH to LOW was detected//Do what you want to do here...ACSR |= B00000011; // Remember top set back the interrupt on rising edge for next ISR}}else //else, if we are into rising edge{if((ACSR & B00100000)){ //If ACO is 1//A change from LOW to HIGH was detected//Do what you want to do here...ACSR |= B00000010; // Remember top set back the interrupt on falling edge for next ISR}}}For clarity, I have a wire connected from the virtual common to digital pin 6 on the uno and a wire connected to analog pin 0 from phase C on the bldc motor. This goes through a voltage divider before connecting to A0. I can read that signal on the scope. I attached a pic of the analog signal coming from Phase C compared to the hall sensor on Phase C.

author: trilerian
author_rank: Regular Contributor
timestamp: June 26, 2023, 09:32:57 pm
content: So I changed the code from pin 13 to pin 4 and then measured on pin 4. I am getting output, but it looks like the input. I expect this to output more of a square wave...

author: m k
author_rank: Super Contributor
timestamp: July 03, 2023, 03:59:04 pm
content: By "default" analog comparator is between AIN0/D6 and AIN1/D7 pins.It's also a single sided system so real negative must be converted.But I don't understand what I'm seeing, what is input or output and is there an external comparison or not.

author: trilerian
author_rank: Regular Contributor
timestamp: July 03, 2023, 04:25:36 pm
content: Quote from: m k on July 03, 2023, 03:59:04 pmI have found new code that I modified. It took me a bit to get sort of an understanding then remove the pwm sections because I don't need that. Also I have commented out some other sections, just haven't removed it completely.Here is the code I am using now.// Test for reading zero crossbyte 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) Future usePORTB = 0x00;Serial.begin (9600);}// Analog comparator ISRISR (ANALOG_COMP_vect) {// BEMF debouncefor(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 functionswitch(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 moduleADCSRB = (1 << ACME);ADMUX = 1; // Select analog channel 1 as comparator negative inputACSR |= 0x03; // Set interrupt on rising edge}/*void BEMF_A_FALLING(){ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ADMUX = 1; // Select analog channel 1 as comparator negative inputACSR &= ~0x01; // Set interrupt on falling edge}*/void BEMF_B_RISING(){ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ADMUX = 2; // Select analog channel 2 as comparator negative inputACSR |= 0x03;}/*void BEMF_B_FALLING(){ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ADMUX = 2; // Select analog channel 2 as comparator negative inputACSR &= ~0x01;}*/void BEMF_C_RISING(){ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ADMUX = 3; // Select analog channel 3 as comparator negative inputACSR |= 0x03;}/*void BEMF_C_FALLING(){ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ADMUX = 3; // Select analog channel 3 as comparator negative inputACSR &= ~0x01;}*/void AH_BL(){// C RisingPORTD &= ~0x18; // D3 and D4 LOWPORTD |= 0x20; // D5 HIGH}/*void AH_CL(){// B FallingPORTD &= ~0x18; // D3 and D4 LOWPORTD |= 0x20; // D5 HIGH}*/void BH_CL(){// A RisingPORTD &= ~0x28; //D3 and D5 LOWPORTD |= 0x10; // D4 HIGH}/*void BH_AL(){// C FallingPORTD &= ~0x28; //D3 and D5 LOWPORTD |= 0x10; //D4 HIGH}*/void CH_AL(){// B RisingPORTD &= ~0x30; //D4 and D5 LOWPORTD |= 0x08; //D3 HIGH}/*void CH_BL(){// A FallingPORTD &= ~0x30; //D4 and D5 LOWPORTD |= 0x08; //D3 HIGH}*/Like I said, I have commented some out, so sorry if it is hard to read.And attached is a pic of the scope output from D3, 4, and 5. The bottom waveform is a hall effect sensor.Once I get the rpm sensors hooked up, my goal is to measure the time difference between the rising edge of a hall sensor and the corresponding rising edge of the bemf of that phase. Then do math to get degrees difference.

author: m k
author_rank: Super Contributor
timestamp: July 08, 2023, 12:32:54 pm
content: So it's working now.You can polish the text if you want.It may also help you understanding things better.Setup procedure happens only once.So all static settings can go there.LikeADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ACSR |= 0x03; // Set interrupt on rising edgeandACSR |= 0x08; // Enable analog comparator interruptThen bldc_move() subroutines can go away, so can commented switch() cases.Likecase 0: PORTD &= ~0x18; PORTD |= 0x20; ADMUX = 3; break; // D3 and D4 LOW // D5 HIGHcase 2:case 4:If you want to stay fast your math should avoid MUL and DIV operations.You still have free analog inputs so you can add an RPM sensor.Then the case line would be the same but with different values.ADMUX is clear, PORTD values are easier to understand in binary.0000 1000 080001 0000 100001 1000 180010 0000 200010 1000 280011 0000 30If other bits are left as they are you need separated set and clear operations.(&= and |= shorthand operators)Third logical operation in use is tilde(~), negation(NOT).There first PORTD change is AND 11100111, so two bits are cleared.For axle angle you can use internal comparator and timer conjunction.Since you know what phase is triggering you can start timer with RPM and stop when next phase happens.

author: trilerian
author_rank: Regular Contributor
timestamp: July 11, 2023, 03:11:50 am
content: Quote from: m k on July 08, 2023, 12:32:54 pmThank you.So using your suggestion I now have the below. I am unsure about what I bolded in your reply. If I remember correctly, I tried commenting out all the cases and it didn't work correctly, so the odd cases just have break; in them. I assume this is because of the six step commutation.// Test for reading zero crossbyte 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;ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ACSR |= 0x03; // Set interrupt on rising edgeACSR |= 0x08; // Enable analog comparator interruptSerial.begin (115200);}// Analog comparator ISRISR (ANALOG_COMP_vect) {// BEMF debouncefor(i = 0; i < 10; 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 functionswitch(bldc_step){case 0: // C RisingPORTD &= ~0x18; // D3 and D4 LOWPORTD |= 0x20; // D5 HIGHADMUX = 3; // Analog comparator pin A3break;case 1:break;case 2: // A RisingPORTD &= ~0x28; // D3 and D5 LOWPORTD |= 0x10; // D4 HIGHADMUX = 1; // Analog comparator pin A1break;case 3:break;case 4: // B RisingPORTD &= ~0x30; // D4 and D5 LOWPORTD |= 0x08; // D3 HIGHADMUX = 2; // Analog comparator pin A2break;case 5:break;}}void loop() {}

author: m k
author_rank: Super Contributor
timestamp: July 11, 2023, 06:25:35 pm
content: Bolded part is mainly just what happens there, for RPM you need a different style of ISR.This ISR in general is also operating a bit upside down, it hangs there as long as is needed to get its wanted state.ISR should be fast and pass away all the time.

author: m k
author_rank: Super Contributor
timestamp: July 15, 2023, 06:23:33 pm
content: Here is something for RPM.It's untested.Code:int what_mux, timeout, rounds;unsigned long counts;void setup() {ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ACSR |= 0x03; // Set interrupt on rising edgeACSR &= ~0x01; // Set interrupt on falling edgeSerial.begin (115200);TCCR1A = 0;TCCR1B = 0;TIMSK1 |= 0x10; // 1A compare interruptOCR1A = 6250; // 0.1s for 16MHz (prescalar 256)pinMode(3, OUTPUT);pinMode(4, OUTPUT);pinMode(5, OUTPUT);pinMode(13, OUTPUT);}ISR(TIMER1_COMPA_vect) {PORTB ^= 0x20; // toggle D13rounds++;if (rounds > 99){ACSR &= ~0x08;TCCR1B = 0;timeout = 5;}}ISR (ANALOG_COMP_vect) {if (what_mux == 4){counts = millis();PORTD &= ~0x38; // D3, D4 and D5 lowwhat_mux = 1;}else{counts = millis();switch(what_mux){case 1: PORTD |= 0x08; break; // D3 highcase 2: PORTD |= 0x10; break; // D4 highcase 3: PORTD |= 0x20; break; // D5 high}timeout = 0;what_mux++;if (what_mux > 3){ACSR &= ~0x08;timeout = 3;}}}loop {counts = 0;counts = 0;counts = 0;counts = 0;what_mux = 4;ADMUX = what_mux;ACSR |= 0x08;timeout = 0;rounds = 0;TCNT1 = 0;TCCR1B = 8 + 4; // timer mode CTC, OCR1A, prescalar 256while (timeout < 2){// waiting for counts// 1st is for hall sensorif ((what_mux < 4) && (timeout < 1)){ACSR &= ~0x08;ADMUX = what_mux;timeout = 1;// if changing mux is noisy// wait hereACSR |= 0x08;}}if (timeout > 3){// error// too many timer roundstimeout = 4;while (timeout > 0){digitalWrite(13, HIGH); delay(300); digitalWrite(13, LOW); delay(100);digitalWrite(13, HIGH); delay(100); digitalWrite(13, LOW); delay(100);if (what_mux > 1) { digitalWrite(13, HIGH); delay(100); digitalWrite(13, LOW); delay(100); }if (what_mux > 2) { digitalWrite(13, HIGH); delay(100); digitalWrite(13, LOW); delay(100); }if (what_mux > 3) { digitalWrite(13, HIGH); delay(100); digitalWrite(13, LOW); delay(100); }timeout--;}}else{// do calculations here// counts are milliseconds// values can overflow, eventually}}Keep in mind that this includes all switching noises.So setting AIN0 somewhere in the middle of high and low would be a filter of some sort.This is also for single edge only.Rising and falling edges are both present in setup because one operation can change only one state of a bit.If rising edge is needed just comment falling edge away.Other approach would be a double action zero crossing.There you make a second signal from zero crossings that do short pulses.Using their falling edges would also mean that changes are not disturbing.Since those short pulses are separated you can also combine them to single signal.Then original zero crossings can also be treated as digital states.

author: trilerian
author_rank: Regular Contributor
timestamp: July 15, 2023, 07:43:48 pm
content: Quote from: m k on July 15, 2023, 06:23:33 pmThank you! I will process this.Here is what I currently have. Uses the analog comparator for the bemf and then using pin 2 and 3 for external interrupts to measure rpm vs the bemf. Pin 3 is already being written high due to the comparator output, so... Anyway, it works at full duty cycle, but when the duty cycle is chopped from the pwm it puts out crazy values. This is in the comparator, and I don't know how to fix it. I am having the same issues with my lm339.Code:// Test for reading zero cross and Timingunsigned long RPMMicros;unsigned long PHMicros;unsigned long prevRPMMicros;unsigned long prevPHMicros;unsigned long RPMDuration;volatile unsigned long PHDuration;unsigned long RPMCount;unsigned long PHCount;unsigned long prevDisplayMillis;unsigned long displayInterval = 1000;unsigned long FREQ;float Timing;unsigned long int RPM;float DEGREES;const float average_of = 1000;//Average of 500 readings for Timing// variables for the RPM ISRvolatile unsigned long RPMisrMicros;volatile unsigned long RPMisrCount;volatile bool RPMnewIsrMicros = false;// variables for the PH ISRvolatile unsigned long PHisrMicros;volatile unsigned long PHisrCount;volatile bool PHnewIsrMicros = false;byte bldc_step = 0, motor_speed;unsigned int i;void setup() {Serial.begin(115200);Serial.println("WMH Racing Timing");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;ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ACSR |= 0x03; // Set interrupt on rising edgeACSR |= 0x08; // Enable analog comparator interruptattachInterrupt(digitalPinToInterrupt(2), RPMSensorISR, RISING);attachInterrupt(digitalPinToInterrupt(3), PHSensorISR, RISING);}// Analog comparator ISRISR (ANALOG_COMP_vect) {// BEMF debouncefor(i = 0; i < 10; 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 functionswitch(bldc_step){case 0: // C RisingPORTD &= ~0x18; // D3 and D4 LOWPORTD |= 0x20; // D5 HIGHADMUX = 3; // Analog comparator pin A3break;case 1:break;case 2: // A RisingPORTD &= ~0x28; // D3 and D5 LOWPORTD |= 0x10; // D4 HIGHADMUX = 1; // Analog comparator pin A1break;case 3:break;case 4: // B RisingPORTD &= ~0x30; // D4 and D5 LOWPORTD |= 0x08; // D3 HIGHADMUX = 2; // Analog comparator pin A2break;case 5:break;}}void loop() {getIsrData();if (millis() - prevDisplayMillis >= displayInterval) {prevDisplayMillis += displayInterval;showData();}}void getIsrData() {if (RPMnewIsrMicros == true) {prevRPMMicros = RPMMicros; // save the previous valuenoInterrupts();RPMMicros = RPMisrMicros;RPMCount = RPMisrCount;RPMnewIsrMicros = false;interrupts();RPMDuration = RPMMicros - prevRPMMicros;FREQ = 1000000/RPMDuration;RPM = FREQ * 60;DEGREES = (RPMDuration*1000)/360;}if (PHnewIsrMicros == true) {prevPHMicros = PHMicros;noInterrupts();PHMicros = PHisrMicros;PHCount = PHisrCount;PHnewIsrMicros = false;interrupts();PHDuration = PHMicros - RPMMicros;Timing = ((PHDuration*1000)/DEGREES)-72;}}void showData() {Serial.println();Serial.println("===============");//Serial.print(" RPM Duration ");//Serial.print(RPMDuration);//Serial.print(" RPM Count ");//Serial.print(RPMCount);//Serial.print(" FREQ ");Serial.print(FREQ);Serial.print(" Hz");//Serial.print("*");Serial.print(" RPM ");Serial.print(RPM);Serial.print(" Timing ");Serial.print(Timing);Serial.print("*");//Serial.print(" Angle ");//Serial.print(PHDuration);//Serial.print(" AVG Timing ");//Serial.print(getTimingAverage(),2);//Serial.print(" DEGREES ");//Serial.print(DEGREES);Serial.println();}void RPMSensorISR() {RPMisrMicros = micros();RPMisrCount ++;RPMnewIsrMicros = true;}void PHSensorISR() {PHisrMicros = micros();PHisrCount ++;PHnewIsrMicros = true;}

author: m k
author_rank: Super Contributor
timestamp: July 16, 2023, 06:07:58 pm
content: My noise reduction interrupt fidling was vanished, added.ACSR &= ~0x08;ADMUX = what_mux;timeout = 1;// if changing mux is noisy// wait hereACSR |= 0x08;Some setups were also missing.pinMode(3, OUTPUT);pinMode(4, OUTPUT);pinMode(5, OUTPUT);My idea was that timing between phases is enough for speed and hall sensor timing compared to phases can give the load.If hall sensor happens to be close to zero crossing a separated sensing should be considered.You've kept the original comparator interrupt handler, it still has both edges.During odd bldc_step it checks that comparator output is high and low during even bldc_step, but interrupt is set to happen only when comparator output becomes high.So when bldc_step is odd the interrupt returns sort of normally, but if comparator saw so short interference pulse that its state was gone before interrupt was handled the handler will wait as long as is needed that the state returns.When bldc_step is even the interrupt is handled differently, it'll wait until comparator state is low, but interrupt happens only when comparator output becomes high.My guess is that original coding idea was to catch all crossings, so interrupt mode is wrong, it should be level change, then both sides would be treated equally.If so thenACSR |= 0x03; // Set interrupt on rising edgeshould change toACSR &= ~0x03; // Comparator Interrupt at level changeCurrently it goes so that first bldc_step is 0 and ADMUX unknown.Then interrupt happens and handler start waiting output low.Code:bldc_step wait ADMUX0 ?low > 31high2low > 13high4low > 25highComparator output is also AIN0 based, so when mux line is higher output is low.Now interrupt is happening when mux line goes lower than AIN0.Seems that you need a new comparator interrupt handler.If you're only using D3 for phases then one phase to D3 interrupt would be better.PutACSR &= ~0x03; // Comparator Interrupt at level changeandADMUX =what everyour mux lineto setup sectionadd a variablecomp_outthen// Analog comparator ISRISR (ANALOG_COMP_vect) {comp_out = ACSR & 0x20;for (i = 0; i < 10; i++){// simple ripple delayif ((ACSR & 0x20) != comp_out) break;}if (i > 9){if (ACSR & 0x20){PORTD |= 8;}else{PORTD &= ~8;}}}E,dam, what ever is of course your mux line.

author: trilerian
author_rank: Regular Contributor
timestamp: July 17, 2023, 10:44:02 pm
content: Quote from: m k on July 16, 2023, 06:07:58 pmIt is going to take me a minute (or more, lol), but I will report back once I do. Thanks

author: m k
author_rank: Super Contributor
timestamp: July 18, 2023, 04:25:31 pm
content: Think one pulse, it has a rising edge and a falling edge.When time is short enough it has only levels, that is not the case here.If rising edge is selected the happening happens only when level is changing from low to high.When you change the mux line you're doing it before comparator, so possibly also generating an edge, that's when old mux line level is different that the new one.Since your comparator is AIN0 based but your signal is sort of AIN1 based you can simply invert the comparator happening.So changing interrupt style from rising edge to falling edge is all it needs, then the situation is again up and up.Possible ripple is always a problem.Here 0x20 inside an interrupt is comparator output ACO, the actual interrupt trigger.So rapid change there indicates an interference of some sort.Ringing is a normal switching byproduct, so a short wait can be all that is needed.Your interrupt has two of those 0x20 checks because it originally handled both edges.There is only one interrupt but 3 phases and 2 edges each, so 6 different situations.1230 1011 1002 1103 0104 0115 001That is why modulo 6 is used but it's an actual division so simple test can be faster.if (bldc_step > 5) bldc_step = 0;But your current system, it is not symmetric.After first unknown interrupt it activates D5 and mux 3, increments bldc_step and start waiting.When mux 3 goes from high to low compared to AIN0 an interrupt happens, so number 1 of upper table.Then bldc_step is odd so first 0x20 check happens, it says that for-loop is waiting as long as ACO is low.But ACO just turned high so no delay.Case for bldc_step=1 is empty so increment bldc_step and back to waiting, mux is still 3 so full wave is needed before next high to low happens.After next interrupt bldc_step is even so second 0x20 check happens, it says that for-loop is waiting as long as ACO is high.Since ACO just turned high an interrupt start waiting its change to low.After that D4 is activated, mux is changed to 1 and bldc_step incremented, so next interrupt is number 3 of upper table.Then another number 3 and finally two times of number 5 with mux 2.So when PWM start slowing down the interrupt start waiting more.When mux line goes low an ACO goes high and interrupt happens, but every other case an interrupt is waiting as long as mux line stays low.My earlier ISR will fix all of those.It just flips D3 when ACO flips.Inverted and without any delays or filterings it's// Analog comparator ISRISR (ANALOG_COMP_vect) {if (ACSR & 0x20){PORTD &= ~8;}else{PORTD |= 8;}}One other thing.When you use Arduino IDE you're at higher level.Means that everything is not as real as you may think.Arduino IDE\lower level program\hardwareInterrupt is low level stuff, lower than normal program execution, actual code execution is the same but inside an interrupt has a priority.More different it is when you deal with things that are not actual code, like millis(), a secondary level timer based on some other timer.So if your code prevents interrupting or stay in one longer than millis() requires it obviously start lagging.If ripple is a problem you can also combine two interrupts, one for the actual thing and other for the delay.Something like stopping comparator interrupt when it happens and starting timer and then when timer interrupt happens stop it and start comparator interrupt.If timing must be fast compared to what MCU can do then something else is better.Code:setup() {TCCR1A = 0;TCCR1B = 8; // timer mode CTC, OCR1ATIMSK1 |= 0x10; // 1A compare interruptOCR1A = 625; // 0.01s for 16MHz (prescalar 256)}ISR (ANALOG_COMP_vect) {ACSR &= ~0x08; // interrupt offif (ACSR & 0x20){PORTD &= ~8;}else{PORTD |= 8;}TCCR1B |= 4; // timer start, prescalar 256}ISR(TIMER1_COMPA_vect) {TCCR1B &= ~0x07; // timer stopACSR |= 0x08; // comparator interrupt on}

author: trilerian
author_rank: Regular Contributor
timestamp: July 21, 2023, 03:13:18 am
content: Quote from: m k on July 18, 2023, 04:25:31 pmI had a chance to tinker with my setup tonight. I am still not quite following what you are explaining. I really appreciate the time you are spending on this, but some of the coding, well, I haven't quite figured out how to think that way yet.Some of the stuff you are saying makes sense, but not all of it.Let me see if I can go through my code and find out where the issues are.So here is what I ended up with tonight, I tried quite a few of the things you suggested, but came back to the following.Code:// Test for reading zero cross and Timingunsigned long RPMMicros;unsigned long PHMicros;unsigned long prevRPMMicros;unsigned long prevPHMicros;unsigned long RPMDuration;volatile unsigned long PHDuration;unsigned long RPMCount;unsigned long PHCount;unsigned long prevDisplayMillis;unsigned long displayInterval = 1000;unsigned long FREQ;float Timing;unsigned long int RPM;float DEGREES;// variables for the RPM ISRvolatile unsigned long RPMisrMicros;volatile unsigned long RPMisrCount;volatile bool RPMnewIsrMicros = false;// variables for the PH ISRvolatile unsigned long PHisrMicros;volatile unsigned long PHisrCount;volatile bool PHnewIsrMicros = false;byte bldc_step = 0, motor_speed;unsigned int i;void setup() {Serial.begin(115200);Serial.println("WMH Racing Timing");DDRD |= 0x38; // Configure pins 3, 4, and 5 as outputs (BEMF Detection)PORTD = 0x00;ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ACSR |= 0x03; // Set interrupt on rising edge//ACSR &= ~0x03; // Comparator Interrupt at level changeACSR |= 0x08; // Enable analog comparator interruptattachInterrupt(digitalPinToInterrupt(2), RPMSensorISR, RISING);attachInterrupt(digitalPinToInterrupt(3), PHSensorISR, RISING);}// Analog comparator ISRISR (ANALOG_COMP_vect) {// BEMF debouncefor(i = 0; i < 10; i++) {if(bldc_step & 1){if(!(ACSR & 0x20)) i -= 1;}else {if((ACSR & 0x20)) i -= 1;}}/*ISR (ANALOG_COMP_vect) {if (ACSR & 0x20){PORTD &= ~8;}else{PORTD |= 8;}*/bldc_move();bldc_step++;if (bldc_step > 5) bldc_step = 0;}void bldc_move(){ // BLDC motor commutation functionswitch(bldc_step){case 0: // C RisingPORTD &= ~0x18; // D3 and D4 LOWPORTD |= 0x20; // D5 HIGHADMUX = 3; // Analog comparator pin A3break;case 1://PORTD |= 0x08; // D3 HIGH//PORTD &= ~0x30; // D4 and D5 LOW//ADMUX = 2;break;case 2: // A RisingPORTD &= ~0x28; // D3 and D5 LOWPORTD |= 0x10; // D4 HIGHADMUX = 1; // Analog comparator pin A1break;case 3://PORTD &= ~0x18; // D3 and D4 LOW//PORTD |= 0x20; // D5 HIGH//ADMUX = 1;break;case 4: // B RisingPORTD &= ~0x30; // D4 and D5 LOWPORTD |= 0x08; // D3 HIGHADMUX = 2; // Analog comparator pin A2break;case 5://PORTD &= ~0x18; // D3 and D4 LOW//PORTD |= 0x20; // D5 HIGH//ADMUX = 3;break;}}void loop() {getIsrData();if (millis() - prevDisplayMillis >= displayInterval) {prevDisplayMillis += displayInterval;showData();}}void getIsrData() {if (RPMnewIsrMicros == true) {prevRPMMicros = RPMMicros; // save the previous valuenoInterrupts();RPMMicros = RPMisrMicros;RPMCount = RPMisrCount;RPMnewIsrMicros = false;interrupts();RPMDuration = RPMMicros - prevRPMMicros;FREQ = 1000000/RPMDuration;RPM = FREQ * 60;DEGREES = (RPMDuration*1000)/360;}if (PHnewIsrMicros == true) {prevPHMicros = PHMicros;noInterrupts();PHMicros = PHisrMicros;PHCount = PHisrCount;PHnewIsrMicros = false;interrupts();PHDuration = PHMicros - RPMMicros;Timing = ((PHDuration*1000)/DEGREES)-72;}}void showData() {Serial.println();Serial.println("===============");//Serial.print(" RPM Count ");//Serial.print(RPMCount);Serial.print(" RPM Duration ");Serial.print(RPMDuration);Serial.print(" FREQ ");Serial.print(FREQ);Serial.print(" Hz");//Serial.print("*");Serial.print(" RPM ");Serial.print(RPM);Serial.print(" Timing ");Serial.print(Timing);Serial.print("*");//Serial.print(" Angle ");Serial.print(" PHDuration ");Serial.print(PHDuration);//Serial.print(" DEGREES ");//Serial.print(DEGREES);Serial.println();}void RPMSensorISR() {RPMisrMicros = micros();RPMisrCount ++;RPMnewIsrMicros = true;}void PHSensorISR() {PHisrMicros = micros();PHisrCount ++;PHnewIsrMicros = true;}So after reading your replies, 30 or 40 times, I finally understood that the bldc move starts with pin D5, 0x20.I tried putting in your ISR*ISR (ANALOG_COMP_vect) {if (ACSR & 0x20){PORTD &= ~8;}else{PORTD |= 8;}I got worse results with this. You can see in my code where I commented it out. I just commented the section above out when I tried it.I did add "if (bldc_step > 5) bldc_step = 0;" in place of the step numbers.Changing the interrupt from the rising edge to a level change also gave worse results. They were close, but fluctuated more.I added code to each of the breaks from the original sketch I used. I tried commenting out different parts of the code, in the end I commented them back out again. All it seemed to do was make the high pulse shorter on my scope. And having the additional ADMUX lines messed with the timing.What I did do though was run a motor at 8 different throttle inputs, named Level1-8. Level1 is the lowest throttle input and Level8 is full throttle. I attached pin D3 to my scope on channel 1, and piggybacked off the rpm sensor on channel 2, which I used to trigger.Channel 2 is on the bottom and is the hall sensor for phase A which also goes into pin D2. Channel 1 on top and it is the comparator output for Phase A. I took a screen shot of the scope output and a screenshot of my serial output for each throttle level. As you can see with the scope pics the comparator output stabilizes at 100% throttle. RPM values are erratic as well, and this I would expect to be more accurate, even at lower duty, but still at 100% it is good. My guess is there is some interrupt conflicts happening somewhere.

author: trilerian
author_rank: Regular Contributor
timestamp: July 21, 2023, 05:04:59 am
content: After some more thought, I think I only need one phase reading as suggested. So I want to get rid of the mux and just use AIN0 or 1? Whichever is pin D7. Then I can leave the adc on, right? Also shouldn’t I be able to read the micros when the rising edge happens from the comparator? If that is the case I don’t need to have the ISR for reading pin 3, because really I am just using that pin for the external interrupt. But to add on to this, I will want to measure hall sensor variance with pin change interrupts. So I will be adding 4, because of pin change and removing 4 from the aco? Is this doable?

author: trilerian
author_rank: Regular Contributor
timestamp: July 21, 2023, 01:08:23 pm
content: This is what I have come up with.I am replacing the admux with AIN1. Set the comparator for rising edge, which I'm pretty sure I will need to switch to the falling edge, we'll see. I think from the posts above that is what I will want in the end since the rising edge is actually the phase signal going below the virtual common. Replace the contents of the comp_vect isr with the contents of PHSensorISR and comment out. Get rid of the bldc move function. Then in the loop check the output of the comparator out and write pin 3 high or low for scope output or LED. And really I can choose a different pin, which I probably will so I can use pin 3 for another external interrupt.I am going to try to compile this and run it later, but I won't have much time to troubleshoot it since I am moving tomorrow. But does this look right?Code:// Test for reading zero cross and Timingunsigned long RPMMicros;unsigned long PHMicros;unsigned long prevRPMMicros;unsigned long prevPHMicros;unsigned long RPMDuration;volatile unsigned long PHDuration;unsigned long RPMCount;unsigned long PHCount;unsigned long prevDisplayMillis;unsigned long displayInterval = 1000;unsigned long FREQ;float Timing;unsigned long int RPM;float DEGREES;// variables for the RPM ISRvolatile unsigned long RPMisrMicros;volatile unsigned long RPMisrCount;volatile bool RPMnewIsrMicros = false;// variables for the PH ISRvolatile unsigned long PHisrMicros;volatile unsigned long PHisrCount;volatile bool PHnewIsrMicros = false;byte bldc_step = 0, motor_speed;unsigned int i;void setup() {Serial.begin(115200);Serial.println("WMH Racing Timing");//DDRD |= 0x38; // Configure pins 3, 4, and 5 as outputs (BEMF Detection)DDRD |= 0x08; // Configure pin 3 as output for scope or LEDPORTD = 0x00;//ADCSRA = (0 << ADEN); // Disable the ADC module//ADCSRB = (0 << ACME);//ACSR |= 0x03; // Set interrupt on rising edge//ACSR &= ~0x03; // Comparator Interrupt at level change//ACSR |= 0x08; // Enable analog comparator interruptACSR =(0 << ACD) | // Analog Comparator: Enabled(0 << ACBG) | // Analog Comparator Bandgap Select: AIN0 is applied to the positive input(0 << ACO) | // Analog Comparator Output: Off(1 << ACI) | // Analog Comparator Interrupt Flag: Clear Pending Interrupt(1 << ACIE) | // Analog Comparator Interrupt: Enabled(0 << ACIC) | // Analog Comparator Input Capture: Disabled(1 << ACIS1) | (1 << ACIS0); // Analog Comparator Interrupt Mode: Comparator Interrupt on Rising Output EdgeattachInterrupt(digitalPinToInterrupt(2), RPMSensorISR, RISING);//attachInterrupt(digitalPinToInterrupt(3), PHSensorISR, RISING);}/*// Analog comparator ISRISR (ANALOG_COMP_vect) {// BEMF debouncefor(i = 0; i < 10; i++) {if(bldc_step & 1){if(!(ACSR & 0x20)) i -= 1;}else {if((ACSR & 0x20)) i -= 1;}}/*ISR (ANALOG_COMP_vect) {if (ACSR & 0x20){PORTD &= ~8;}else{PORTD |= 8;}*/bldc_move();bldc_step++;if (bldc_step > 5) bldc_step = 0;}void bldc_move(){ // BLDC motor commutation functionswitch(bldc_step){case 0: // C RisingPORTD &= ~0x18; // D3 and D4 LOWPORTD |= 0x20; // D5 HIGHADMUX = 3; // Analog comparator pin A3break;case 1://PORTD |= 0x08; // D3 HIGH//PORTD &= ~0x30; // D4 and D5 LOW//ADMUX = 2;break;case 2: // A RisingPORTD &= ~0x28; // D3 and D5 LOWPORTD |= 0x10; // D4 HIGHADMUX = 1; // Analog comparator pin A1break;case 3://PORTD &= ~0x18; // D3 and D4 LOW//PORTD |= 0x20; // D5 HIGH//ADMUX = 1;break;case 4: // B RisingPORTD &= ~0x30; // D4 and D5 LOWPORTD |= 0x08; // D3 HIGHADMUX = 2; // Analog comparator pin A2break;case 5://PORTD &= ~0x18; // D3 and D4 LOW//PORTD |= 0x20; // D5 HIGH//ADMUX = 3;break;}}*/void loop() {getIsrData();if (millis() - prevDisplayMillis >= displayInterval) {prevDisplayMillis += displayInterval;showData();}if(ACSR & (1 << ACO)) // check status of comparator output flagdigitalWrite(3, HIGH); // and mirror it to Pin 3elsedigitalWrite(3, LOW);}void getIsrData() {if (RPMnewIsrMicros == true) {prevRPMMicros = RPMMicros; // save the previous valuenoInterrupts();RPMMicros = RPMisrMicros;RPMCount = RPMisrCount;RPMnewIsrMicros = false;interrupts();RPMDuration = RPMMicros - prevRPMMicros;FREQ = 1000000/RPMDuration;RPM = FREQ * 60;DEGREES = (RPMDuration*1000)/360;}if (PHnewIsrMicros == true) {prevPHMicros = PHMicros;noInterrupts();PHMicros = PHisrMicros;PHCount = PHisrCount;PHnewIsrMicros = false;interrupts();PHDuration = PHMicros - RPMMicros;Timing = ((PHDuration*1000)/DEGREES)-72;}}void showData() {Serial.println();Serial.println("===============");//Serial.print(" RPM Count ");//Serial.print(RPMCount);Serial.print(" RPM Duration ");Serial.print(RPMDuration);Serial.print(" FREQ ");Serial.print(FREQ);Serial.print(" Hz");//Serial.print("*");Serial.print(" RPM ");Serial.print(RPM);Serial.print(" Timing ");Serial.print(Timing);Serial.print("*");//Serial.print(" Angle ");Serial.print(" PHDuration ");Serial.print(PHDuration);//Serial.print(" DEGREES ");//Serial.print(DEGREES);Serial.println();}void RPMSensorISR() {RPMisrMicros = micros();RPMisrCount ++;RPMnewIsrMicros = true;}/*void PHSensorISR() {PHisrMicros = micros();PHisrCount ++;PHnewIsrMicros = true;}*/ISR(ANALOG_COMP_vect ) {PHisrMicros = micros();PHisrCount ++;PHnewIsrMicros = true;}

author: m k
author_rank: Super Contributor
timestamp: July 21, 2023, 05:22:40 pm
content: Must check the last one later.Somehow I though you have an external axle magnet RPM thing.So no load from hall sensor.All interrupts are hardware specific, micros() even more, if possible.When hardware is slow microseconds start happening overly fast.For 16MHz machine a microsecond is 1/16 of everything, so even updating that kind of thing is a huge load.Reference says that for 16MHz Arduino boards micros() resolution is 4us, it's still much, its value must still be updated.There are other board versions but with earlier comparator interrupt regular micros() will fail for sure, the timer itself is external so it is not failing, only updates are missing, so values are not correct.But first things first.If you can't trust anything drop everything.Since you have ACO you can do polling, interrupt is not needed.Below is putting mux 4 to D5.If loop is slow and pulses are short some may be missed.Your scope has channels, so put mux input also somewhere.AIN1 is no different, it's just a different pin, if mux line is static.Then finally put hall sensor through comparator.Code:unsigned long change_1, change_2;setup() {DDRD |= 0x38;ADCSRA = (0 << ADEN);ADCSRB = (1 << ACME);ADMUX = 4;Serial.begin(115200);change_1 = micros();}next_change() {while (PIND == ACSR & 0x20) { } // just waitingPORD = ACSR & 0x20; // ACO to D5}loop() {next_change();change_2 = micros();Serial.print(change_2 - change_1);Serial.print(" ");next_change();change_1 = micros();Serial.println(change_1 - change_2);}Earlier first picture indicates that your comparator may have some ripple, but its timing indicates something else.Next picture is completely different, it and next seem to have some sort of a startup.Last picture has 1/3 duty cycle.2nd last seems to have a double frequency.Maybe faster X-axle would be more revealing.Next is not so time critical version of the upper loop.I guess you don't need rotation updates when you do other things.Code:unsigned long change_1, change_2, change_3;loop() {next_change();change_1 = micros();next_change();change_2 = micros();next_change();change_3 = micros();Serial.print(change_2 - change_1);Serial.print(" ");Serial.println(change_3 - change_2);}

author: trilerian
author_rank: Regular Contributor
timestamp: July 21, 2023, 11:21:51 pm
content: Quote from: m k on July 21, 2023, 05:22:40 pmI tried to put your code in and it was throwing some errors besides the easy ones like the missing "void", but because I am moving tomorrow my troubleshooting for the night is done.I ended up with a slightly modified version of what I posted earlier, except that I am using the falling edge of the bemf signal. What is hilarious (I guess that really would be expected) is how similar this now looks to the output of the lm339, except in reverse. So that is something, but still only usable at 100% throttle. I added the bemf signal from Phase A as well. So this output below is D3 output from comparator, Hall sensor from phase A, and bemf from phase A. Trigger on the hall sensor since I can use the ground in the circuit.Oh, the code...Code:// Test for reading zero cross and Timingunsigned long RPMMicros;unsigned long PHMicros;unsigned long prevRPMMicros;unsigned long prevPHMicros;unsigned long RPMDuration;volatile unsigned long PHDuration;unsigned long RPMCount;unsigned long PHCount;unsigned long prevDisplayMillis;unsigned long displayInterval = 1000;unsigned long FREQ;float Timing;unsigned long int RPM;float DEGREES;// variables for the RPM ISRvolatile unsigned long RPMisrMicros;volatile unsigned long RPMisrCount;volatile bool RPMnewIsrMicros = false;// variables for the PH ISRvolatile unsigned long PHisrMicros;volatile unsigned long PHisrCount;volatile bool PHnewIsrMicros = false;void setup() {Serial.begin(115200);Serial.println("WMH Racing Timing");DDRD |= 0x08; // Configure pin 3 as output for scope or LEDPORTD = 0x00;ACSR =(0 << ACD) | // Analog Comparator: Enabled(0 << ACBG) | // Analog Comparator Bandgap Select: AIN0 is applied to the positive input(0 << ACO) | // Analog Comparator Output: Off(1 << ACI) | // Analog Comparator Interrupt Flag: Clear Pending Interrupt(1 << ACIE) | // Analog Comparator Interrupt: Enabled(0 << ACIC) | // Analog Comparator Input Capture: Disabled(1<<ACIS1) | (0<<ACIS0);//interrupt on falling output edge//(1<<ACIS1) | (1<<ACIS0);//interrupt on rising output edgeattachInterrupt(digitalPinToInterrupt(2), RPMSensorISR, RISING);}void loop() {getIsrData();if (millis() - prevDisplayMillis >= displayInterval) {prevDisplayMillis += displayInterval;showData();}if(ACSR & (1 << ACO)) // check status of comparator output flagdigitalWrite(3, LOW); // and mirror it to Pin 3elsedigitalWrite(3, HIGH);}void getIsrData() {if (RPMnewIsrMicros == true) {prevRPMMicros = RPMMicros; // save the previous valuenoInterrupts();RPMMicros = RPMisrMicros;RPMCount = RPMisrCount;RPMnewIsrMicros = false;interrupts();RPMDuration = RPMMicros - prevRPMMicros;FREQ = 1000000/RPMDuration;RPM = FREQ * 60;DEGREES = (RPMDuration*1000)/360;}if (PHnewIsrMicros == true) {prevPHMicros = PHMicros;noInterrupts();PHMicros = PHisrMicros;PHCount = PHisrCount;PHnewIsrMicros = false;interrupts();PHDuration = PHMicros - RPMMicros;Timing = ((PHDuration*1000)/DEGREES)-72;}}void showData() {Serial.println();Serial.println("===============");//Serial.print(" RPM Count ");//Serial.print(RPMCount);Serial.print(" RPM Duration ");Serial.print(RPMDuration);Serial.print(" FREQ ");Serial.print(FREQ);Serial.print(" Hz");//Serial.print("*");Serial.print(" RPM ");Serial.print(RPM);Serial.print(" Timing ");Serial.print(Timing);Serial.print("*");//Serial.print(" Angle ");Serial.print(" PHDuration ");Serial.print(PHDuration);//Serial.print(" DEGREES ");//Serial.print(DEGREES);Serial.println();}void RPMSensorISR() {RPMisrMicros = micros();RPMisrCount ++;RPMnewIsrMicros = true;}ISR(ANALOG_COMP_vect ) {PHisrMicros = micros();PHisrCount ++;PHnewIsrMicros = true;}

author: m k
author_rank: Super Contributor
timestamp: July 22, 2023, 07:04:06 pm
content: If mux pin is not changing it's just like AIN1, just a different pin.It's also still same if it is changed in a controllable way, so if you need you can return there, but change ISR style.Interrupts are fine.Comparator output status thing can be(interrupting pin is 2)if (ACSR & 0x20)PORTD |= 8; // pin up, 4 for pin 2elsePORTD &= ~8; // pin down, 4 for pin 2Current update position can put RPM and comparataor out of sync, RPM interrupt is possibly generated much later, and with some bad luck not at all.Put another pin manipulation thing inside and to the beginning of the comparator interrupt, then RPM interrupt is happening right after comparator interrupt, if needed.Since comparator interrupt is falling edge and ACO up put pin up all polarities remain the same, quite many inverting possibilities.But since interrupt is only happening during one edge you need both manipulators, the loop one is where digital pin returns to its other state and so can interrupt again.Better would be that set happens in first interrupt and reset in second.Last picture is like comparator is doing one full wave around a beginning of BEMF flip, but 1st short part is missing.Can't say from where that short pulse comes, it must be external and real.There also seems to be quite accurate timing between signals when hall sensor goes down.Seems that your only possibility is a delay, then you can get a stable ending edge, but only that.There you start timer every time comparator flips, so comparator is in toggle mode.Then comparator interrupt starts timer, if comparator is flipping new interrupt happens and timer is started again.When comparator is not flipping timer reaches its limit and timer interrupt happens, once, it must disable itself.Interrupt doesn't know ACO state, so test must be added.Then if ACO is correct timer interrupt changes comparator to correct edge mode.Finally comparator interrupt happens and the correct moment is present, then interrupt must also change its mode back to toggle.

author: trilerian
author_rank: Regular Contributor
timestamp: July 24, 2023, 09:02:39 pm
content: Quote from: m k on July 22, 2023, 07:04:06 pmIt isn't that I need to use AIN1, it is if I am only going to use one bemf signal, then may as well use the pin that doesn't disable the ADC. The hall effect sensors can use other hardware interrupts. I think I am going to try a low pass filter on the bemf signal going into AIN1. I created one when I was using the lm339 and it helps the input, but rising edge became a problem. Maybe falling edge will be better, it definitely is sharper.Alright, so what is the following actually doing?if (ACSR & 0x20)PORTD |= 8; // pin up, 4 for pin 2elsePORTD &= ~8; // pin down, 4 for pin 2My understanding is that is saying if "Analog comparator and AIN 0 (pin d6), interrupt triggers" then "write pin D3 HIGH, else D3 LOW". Which I am doing specifically to see the output on my scope. Why would I want to make that pin 2? Pin 2 is the input from 1 of the 3 hall effect sensors. Right now I am only reading from 1 of them. I will read from all 3 at some point. I am just going to start with that simple line. I feel like I need to go in baby steps here.Thanks

author: m k
author_rank: Super Contributor
timestamp: July 25, 2023, 02:47:10 pm
content: Comparator output ACO and interrupt ACI are different.ACO is read only but ACI can be written.So you can generate that interrupt when you like but comparator output is what it is.ACO can of course also be the ACI trigger.ACIE then is enable for the interrupt and ACIC an input capture enable for timer, so internal low pass.So close but not exactly how you put it.It says that put D3 high if ACO is high, otherwise put D3 low.It has nothing to do with interrupt, interrupt is following ACO only if it set to do so.In your case where only one edge is triggering the interrupt happens only once but comparing all the time and it is also seeing the other edge, the not interrupt triggering one.It's also possible that interrupt happens in the middle of that compare part.Pin 2 is mentioned just in case, just because your interrupt was attached to pin 2 and before the attached interrupt was triggered by the comparator.It has nothing to do with anything that must be.Putting the whole compare thing in the ISR has nothing to do with the interrupt trigger either.It's there for convenience, both edges are covered.Inverting is also easy, just swap those value setting parts.Rising edge or starting edge is problematic because it happens first.Falling edge or ending edge is better because you already know when the other edge happened.Your low pass filter is an analog way of dropping too short pulses out, that MCU internal and digital timer thing is made for just that.If you have hard time following what happens start making flowcharts, professionals do the same.

author: trilerian
author_rank: Regular Contributor
timestamp: July 27, 2023, 02:31:27 am
content: Quote from: m k on July 25, 2023, 02:47:10 pmOk, I think I follow all that. Now I understand that the ACO out and the interrupt are different, although I can create an interrupt from making D3 high or use the ACIE and the comp-vect. So here we go...I placedif (ACSR & 0x20)PORTD |= 8; // D3 HIGHelsePORTD &= ~8; // D3 LOWinside of comparator interrupt byISR(ANALOG_COMP_vect ) {if (ACSR & 0x20)PORTD |= 8; // D3 HIGHelsePORTD &= ~8; // D3 LOWPHisrMicros = micros();PHisrCount ++;PHnewIsrMicros = true;So, write D3 high or low and record time and add count and reset PHnewIsrMicros.So this would be using the ACIE to set the pins instead of the loop. At full throttle, I lose visibility on my scope, also the FREQ measurement is off, so this statement being in the interrupt causes some sort of issue, not sure what. Putting it in the loop though doesn't cause any issues except the false edges for the bemf sensing. But at full throttle that is mostly fine. I put an if statement in for reading the timing that specifies phduration be above a certain value, which works great for full throttle. I think I am going to make that a bigger if, else if statement and get different frequency ranges and phduration, this could be maybe a software check for the edge. Since really the output isn't time critical, I will be updating at most every 500 milliseconds, I think it should be fine. I will try to learn more timer methods as I progress, and continue to look at your posts for inspiration.Also, I guess I don't understand why it is bad for the comparator to compare all edges and only interrupt on one. If the comparator is going to do what it does no matter what, and the comparator interrupt only interrupts on the rising edge, I don't understand why that can be badAlso, I am going to want to average some of these values and output the average. I always struggle with this too, even though it seems easy.Here is current code, this has so far worked the best.Code:// Test for reading zero cross and Timingunsigned long RPMMicros;unsigned long PHMicros;unsigned long prevRPMMicros;unsigned long prevPHMicros;unsigned long RPMDuration;volatile unsigned long PHDuration;unsigned long RPMCount;unsigned long PHCount;unsigned long prevDisplayMillis;unsigned long displayInterval = 1000;unsigned long FREQ;float Timing;unsigned long int RPM;float DEGREES;// variables for the RPM ISRvolatile unsigned long RPMisrMicros;volatile unsigned long RPMisrCount;volatile bool RPMnewIsrMicros = false;// variables for the PH ISRvolatile unsigned long PHisrMicros;volatile unsigned long PHisrCount;volatile bool PHnewIsrMicros = false;void setup() {Serial.begin(115200);Serial.println("WMH Racing Timing");DDRD |= 0x08; // Configure pin 3 as output for scope or LEDPORTD = 0x00;ACSR =(0 << ACD) | // Analog Comparator: Enabled(0 << ACBG) | // Analog Comparator Bandgap Select: AIN0 is applied to the positive input(0 << ACO) | // Analog Comparator Output: Off(1 << ACI) | // Analog Comparator Interrupt Flag: Clear Pending Interrupt(1 << ACIE) | // Analog Comparator Interrupt: Enabled(0 << ACIC) | // Analog Comparator Input Capture: Disabled(1<<ACIS1) | (0<<ACIS0);//interrupt on falling output edge//(1<<ACIS1) | (1<<ACIS0);//interrupt on rising output edgeattachInterrupt(digitalPinToInterrupt(2), RPMSensorISR, RISING);}void loop() {getIsrData();if (millis() - prevDisplayMillis >= displayInterval) {prevDisplayMillis += displayInterval;showData();}if (ACSR & 0x20)PORTD |= 8; // D3 HIGHelsePORTD &= ~8; // D3 LOW}void getIsrData() {if (RPMnewIsrMicros == true) {prevRPMMicros = RPMMicros; // save the previous valuenoInterrupts();RPMMicros = RPMisrMicros;RPMCount = RPMisrCount;RPMnewIsrMicros = false;interrupts();RPMDuration = RPMMicros - prevRPMMicros;FREQ = 1000000/RPMDuration;RPM = FREQ * 60;DEGREES = (RPMDuration*1000)/360;}if (PHnewIsrMicros == true) {prevPHMicros = PHMicros;noInterrupts();PHMicros = PHisrMicros;PHCount = PHisrCount;PHnewIsrMicros = false;interrupts();PHDuration = PHMicros - RPMMicros;if (PHDuration > 500) {Timing = ((PHDuration*1000)/DEGREES)-70;}}}void showData() {Serial.println();Serial.println("===============");//Serial.print(" RPM Count ");//Serial.print(RPMCount);Serial.print(" RPM Duration ");Serial.print(RPMDuration);Serial.print(" FREQ ");Serial.print(FREQ);Serial.print(" Hz");//Serial.print("*");Serial.print(" RPM ");Serial.print(RPM);Serial.print(" Timing ");Serial.print(Timing);Serial.print("*");//Serial.print(" Angle ");Serial.print(" PHDuration ");Serial.print(PHDuration);//Serial.print(" DEGREES ");//Serial.print(DEGREES);Serial.println();}void RPMSensorISR() {RPMisrMicros = micros();RPMisrCount ++;RPMnewIsrMicros = true;}ISR(ANALOG_COMP_vect ) {PHisrMicros = micros();PHisrCount ++;PHnewIsrMicros = true;}

author: m k
author_rank: Super Contributor
timestamp: July 27, 2023, 04:34:34 pm
content: There are no real good or bad thing per se, just some timing and accuracy.But some basics first, since you will need them a lot, better make'em clear.if (ACSR & 0x20)There are boolean and bitwise operations.if () result is boolean.& is bitwise when && is boolean.So first 8bit ACSR and 20hex are combined.xxxx xxxx0010 0000 ANDvvvv vvvv00x0 0000So ACO bit remains up if it is up, other bits wont survive.Compiler will add needed amount of front zeros depending how wide the other part is.If both parts are literal numbers the width is some default, there are cases where default width can be wrong so use static width when it has a meaning.If you'd be ORing that 20hex you'd set that ACO bit.So OR is for setting and AND is for clearing.Then the result in brackets, the value is either 20hex or zero.How it changes to boolean, the solution is that there really are no such thing as TRUE, it's actually NOT FALSE.It goes to processor and its zero flag, that can be tested, so zero is zero and everything else is not zero.Then finallyPORTD &= ~8There are two bitwise things, '&' and '~', 'and' and invert, '!' would be boolean not.So taking shorthand away isPORTD = PORTD & ~8If needed the compiler uses its internal scratch variable for intermediate result.Then '~', inverting 8bit 8(B00001000), soPORTD = PORTD & B11110111So clearing D3.Then back to the real thing.Not ACIE, that's only enabling the ACI.ACI is the actual interrupt trigger bit, it's also automatic with regular Arduino, means that system will clear it when the correct time comes, but that is hardware dependent, so check it if situation is unknown.If D3 is an independent ACO indicator you can leave the compare section out and do it elsewhere.If D3 is an attached interrupt launcher then you obviously keep it.So if your pin 2 has an external input then that is that and no software connection is needed.What you changed with the compare in the ISR is just a moment when the D3 change happens.So without compare in the ISR your loop speed is defining how fast D3 can change.With compare in the ISR you're possibly changing the duty cycle of D3, loop and ISR are timed differently and interrupt can act faster than your loop.Since interrupt is happening only when one edge happens it is also putting D3 state accordingly, so the same in all cases.If you need a "real" ACO indicator you must put the interrupt to toggle mode and adjust other updates.Then you can also leave loop compare part off.And then you can have a situation where all edges are toggling D3 but only one is updating the phase thing.setup() {// (0<<ACIS1) | (0<<ACIS0); // toggle// (0<<ACIS1) | (1<<ACIS0); // reserved// (1<<ACIS1) | (0<<ACIS0); // falling// (1<<ACIS1) | (1<<ACIS0); // risingACSR &= ~3;}ISR(ANALOG_COMP_vect ) {if (ACSR & 0x20){// ACO going upPORTD |= 8; // D3 HIGH// phase stuff for rising edgePHisrMicros = micros();PHisrCount ++;PHnewIsrMicros = true;}else{// ACO going downPORTD &= ~8; // D3 LOW// phase stuff for falling edge}}Keep in mind that ACO is always AIN0 > AIN1, active edge can be different but the actual thing remains.This inverting possibility here, there and everywhere can be a bit confusing sometimes.But none of this is helping you with too fast pulses.Your loop time can obviously do that but it is inaccurately timed, it doesn't know when the thing happened, only how it is when its compare time happens.If loop time is long you'll see nice and clean D3 pulses put you can't say what kind of an on going pulse there is.Above ISR will also update you phase counter, so you can check how many edges there have been.If counter difference is more than one you can't use its recorded time since it's not the original one anymore.So finally the real low pass filter is needed, analog or digital.You can try to match your loop time so that all short pulses will leave out but then you're stuck with it.One other possibility is to but a delay to the beginning of the loop and wait until long pulse appears.Then you don't use comparator ISR at all.loop () {while (1) {while (1) { if (ACSR & 0x20) break; } // wait until ACO is highPHisrMicros = micros();while (1) { if (!(ACSR & 0x20)) break; } // wait until ACO is lowfalling_moment = micros();// unsigned values have no negatives so difference is always positiveif ((falling_moment - PHisrMicros) > enough_time) break;}}Since RPM pulse is rising edge it happened long before falling edge of ACO and ending of above, so no problems there but situation will be different if falling edge is used as a trigger.Can't say what affects FREQ.One possibility is that the compare part is slow and happens so often that some RPM interrupts are lost.

author: trilerian
author_rank: Regular Contributor
timestamp: July 28, 2023, 02:30:13 pm
content: Quote from: m k on July 27, 2023, 04:34:34 pmEverything in this last post, it makes sense, how about that!But question on rising edge vs falling edge, vs toggle. So toggle there will be 2 interrupts, both rising edge and falling edge, a lot like a pin change? Does the overhead of the second interrupt cause issues? I am going to get rid of the pin D3 output because I am going to connect another hall effect sensor, I may write to some other pin for an LED, or if I am feeling quite spirited an LED matrix. I am also going to use another digital pin and connect the last hall effect sensor. D3 I can use the external interrupt and do rising edge again, but the last pin will have to be a pin change and I have to have the interrupt for both edges and figure out which one triggered it. So if I use the toggle, I will have a total of 6 interrupts. Without the toggle it will be 5. So is this a trial and error type of situation?I made a sort of software filter, it was working pretty good in testing last night, so I am hopefulCurrent codeCode:// Test for reading zero cross and Timingunsigned long RPMMicros;unsigned long PHMicros;unsigned long prevRPMMicros;unsigned long prevPHMicros;unsigned long RPMDuration;volatile unsigned long PHDuration;unsigned long RPMCount;unsigned long PHCount;unsigned long prevDisplayMillis;unsigned long displayInterval = 1000;double FREQ;double Timing;long int RPM;double DEGREES;// variables for the RPM ISRvolatile unsigned long RPMisrMicros;volatile unsigned long RPMisrCount;volatile bool RPMnewIsrMicros = false;// variables for the PH ISRvolatile unsigned long PHisrMicros;volatile unsigned long PHisrCount;volatile bool PHnewIsrMicros = false;void setup() {Serial.begin(115200);Serial.println("WMH Racing Timing");pinMode(2, INPUT_PULLUP);//DDRD |= 0x08; // Configure pin 3 as output for scope or LED//PORTD = 0x00;ACSR =(0 << ACD) | // Analog Comparator: Enabled(0 << ACBG) | // Analog Comparator Bandgap Select: AIN0 is applied to the positive input(0 << ACO) | // Analog Comparator Output: Off(1 << ACI) | // Analog Comparator Interrupt Flag: Clear Pending Interrupt(1 << ACIE) | // Analog Comparator Interrupt: Enabled(0 << ACIC) | // Analog Comparator Input Capture: Disabled// (1<<ACIS1) | (0<<ACIS0); //interrupt on falling output edge(1<<ACIS1) | (1<<ACIS0); //interrupt on rising output edgeattachInterrupt(digitalPinToInterrupt(2), RPMSensorISR, RISING);}void loop() {getIsrData();if (millis() - prevDisplayMillis >= displayInterval) {prevDisplayMillis += displayInterval;showData();}/*if(ACSR & (1 << ACO)) // check status of comparator output flagdigitalWrite(3, LOW); // and mirror it to Pin 3elsedigitalWrite(3, HIGH);*/}void getIsrData() {if (RPMnewIsrMicros == true) {prevRPMMicros = RPMMicros; // save the previous valuenoInterrupts();RPMMicros = RPMisrMicros;RPMCount = RPMisrCount;RPMnewIsrMicros = false;interrupts();RPMDuration = RPMMicros - prevRPMMicros;FREQ = 1000000.00/RPMDuration;RPM = FREQ * 60;DEGREES = (RPMDuration*1000)/360;}if (PHnewIsrMicros == true) {prevPHMicros = PHMicros;noInterrupts();PHMicros = PHisrMicros;PHCount = PHisrCount;PHnewIsrMicros = false;interrupts();PHDuration = PHMicros - RPMMicros;if ((FREQ >= 50) && (FREQ <= 100) && (PHDuration >= 1700) && (PHDuration <= 2000))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 100) && (FREQ <= 150) && (PHDuration >= 824) && (PHDuration <= 1300))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 150) && (FREQ <= 200) && (PHDuration >= 552) && (PHDuration <= 900))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 200) && (FREQ <= 250) && (PHDuration >= 420) && (PHDuration <= 780))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 250) && (FREQ <= 300) && (PHDuration >= 328) && (PHDuration <= 652))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 300) && (FREQ <= 350) && (PHDuration >= 276) && (PHDuration <= 556))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 350) && (FREQ <= 400) && (PHDuration >= 240) && (PHDuration <= 488))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 400) && (FREQ <= 450) && (PHDuration >= 208) && (PHDuration <= 432))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 450) && (FREQ <= 500) && (PHDuration >= 184) && (PHDuration <= 392))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 500) && (FREQ <= 550) && (PHDuration >= 164) && (PHDuration <= 356))Timing = ((PHDuration*1000)/DEGREES)-10;}}void showData() {Serial.println();Serial.println("===============");//Serial.print(" RPM Count ");//Serial.print(RPMCount);Serial.print(" RPM Duration ");Serial.print(RPMDuration);Serial.print(" FREQ ");Serial.print(FREQ, 2);Serial.print(" Hz");//Serial.print("*");Serial.print(" RPM ");Serial.print(RPM);Serial.print(" Timing ");Serial.print(Timing);Serial.print("*");//Serial.print(" Angle ");Serial.print(" PHDuration ");Serial.print(PHDuration);//Serial.print(" DEGREES ");//Serial.print(DEGREES);Serial.println();}void RPMSensorISR() {RPMisrMicros = micros();RPMisrCount ++;RPMnewIsrMicros = true;}ISR(ANALOG_COMP_vect ) {PHisrMicros = micros();PHisrCount ++;PHnewIsrMicros = true;}Please comment on the following code, this is my software filter. I need to tune it a bit still, but it works as intended. I just don't know if this is the best way to write it.Code:if ((FREQ >= 50) && (FREQ <= 100) && (PHDuration >= 1700) && (PHDuration <= 2000))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 100) && (FREQ <= 150) && (PHDuration >= 824) && (PHDuration <= 1300))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 150) && (FREQ <= 200) && (PHDuration >= 552) && (PHDuration <= 900))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 200) && (FREQ <= 250) && (PHDuration >= 420) && (PHDuration <= 780))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 250) && (FREQ <= 300) && (PHDuration >= 328) && (PHDuration <= 652))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 300) && (FREQ <= 350) && (PHDuration >= 276) && (PHDuration <= 556))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 350) && (FREQ <= 400) && (PHDuration >= 240) && (PHDuration <= 488))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 400) && (FREQ <= 450) && (PHDuration >= 208) && (PHDuration <= 432))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 450) && (FREQ <= 500) && (PHDuration >= 184) && (PHDuration <= 392))Timing = ((PHDuration*1000)/DEGREES)-10;else if((FREQ >= 500) && (FREQ <= 550) && (PHDuration >= 164) && (PHDuration <= 356))Timing = ((PHDuration*1000)/DEGREES)-10;

author: m k
author_rank: Super Contributor
timestamp: July 28, 2023, 07:50:12 pm
content: Interrupts are independent so they must be able to act together.If not the MCU will never reach the market.There are some limits, of course.Interrupts have priorities, so some lower priority ones can be lost by the definition.Equal priorities then will need something extra for the selection.Simple MCU system is many times so that system will check if any of the interrupt flags are up and continue from there.So only question is how fast.If interrupt must happen faster that MCU can serv then MCU must be changed.If program execution time is too long then selection of options is a bit wider.In this case it's possible that freely rolling interrupt system is much more difficult than the alternative.Since this is not time critical, at least overall, you can slice time to interrupting and counting period.Then interrupting would take one axle revolution and the rest as long as is needed.Trial and error will come from unknown timing, like short pulses here.Since you don't know their lengths there are no other way.It's also possible that some rogue pulses happen, but that's just electronics.The code.Nested things are always a bit nasty.If first thing happens everything is fine but if chain must go to the end there can be problems.If your code can make it there are no real matter how it does it.But practically whole timer system of these MCUs are made for PWM so hardware alternative is available if needed.Few double checks your code has.If it's not under 101 first time...if ((FREQ > 49) && (FREQ < 551)){if (FREQ < 101){}elseif (FREQ < 151){}elseif (FREQ < 201){}...elseif (FREQ < 501){}elseif ((PHDuration >= 164) && (PHDuration <= 356)){}}Missing last FREQ check is already done earlier.Only possible optimization with nested things is to do more frequent ones first.If here the last one is rare its test must be taken away from the beginning.Since you now have only one phase you also have only one long pulse.With three phases you can have a situation where short and long pulses happen together.There mux will be spot on, after one long pulse ends another mux line is selected and wait for next long pulse can start.You can of course do that also with digital pins and external comparators, polling is also fine, interrupts are not rain makers.

author: trilerian
author_rank: Regular Contributor
timestamp: August 07, 2023, 01:49:46 am
content: So after a lot of testing, I just don't think the 328p is up to the task I need it to do. I can get the time between the RPM pulse and the bemf, but anytime I add to this with other sensors and averaging, it goes to crap.These are the chips that the 3 commercially available units use.Silicone Labs C8051F41xMicrochip ATXMEGA256A3UST STM32F401Here are the dev boards I haveArduino Uno and Nano's 328P - Doesn't seem to be enough.Arduino Mega 2560 - Probably the same as above, and doesn't have AIN1 for comparator broke out.Arduino Uno Rev 4 minama ra4m1 - has opp amp built in instead of comparator. Still limited on interrupt pins.Arduino Nano ESP 32 - new board with lots of timers and comparator.ESP Wroom ESP32 -STM32 Nucleo - STM32F446 - Obviously this will work since one of the better available devices use the f401.I think I want the ESP32 to work though, much cheaper and lots of info out there for building a board with one.While I have been tinkering with programming the 328P chips, I know nothing about the esp32. If I use the arduino ISE, do I lose functionality with those boards?So total, these are my requirements.Measure the phase shift between a hall sensor (RPM) and the bemf. This give effective can timing for a bldc.Measure the phase shift between the hall effect sensors, they should be 120° apart, but they can vary. I would like to measure this with 0.1° resolution, along with timing.Needs to be able to measure the battery voltage attached through the system and average.Needs to be able to measure the current in series between the battery and the controller via an Allegro current hall effect sensor.I would like to be able to use this with a flywheel dyno for read out with a sample rate over 100hz. Although I am using the hall effect sensor to measure RPM, I may need to use an optical sensor on the flywheel instead.Motors that I am using can get up to 60,000 rpm or 1000 hz. Current can get up to 200 amps.I am going back to the basics here. I am putting my code I am using right now to measure RPM, current, and voltage. This actually works pretty well using the 328p chip. I tried adding interrupts for the rpm and it starts having issues over 15000 rpm. I have changed the current averages down to 25 and voltage to 10, still have issues. I did run the code without using any averages and got dyno readings with a flywheel that were close to a production unit, so I know it can be done, but I am pretty sure the 328p is limiting me here.You have been a huge help in this journey so far, and I understand a lot more about the comparator and interrupts thanks to you.Code:Update logs2023-Aug-3 Revision 3.6 - Changed to Interrupts for RPM2023-Aug-4 Revision 4.0 - Interrupts cause issue over 16000 RPM, changing back to pulseIn*/#include <Wire.h>#include <hd44780.h> // main hd44780 header#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class headerhd44780_I2Cexp lcd; // declare lcd object: auto locate & auto config expander chip// LCD geometryconst int LCD_COLS = 16;const int LCD_ROWS = 2;// Sample Rateunsigned long displayInterval = 100;unsigned long prevDisplayMillis;//Currentconst int VIN = A0; // define the Arduino pin A0 as voltage input (V in)const float average_of_I = 75; // Number of measurements to use for averagefloat _outVoltage; // Is the voltage after measurements that needs to be divided by the sensitivityfloat _current; // point in time current valuefloat SENS = 0.040; // Sensitivity of the current sensor you are using mv/afloat offset = 0.5; // Adjust offset to zero the current reading.float QOV = 2.4964; // Measure the output of the current sensor with no load and enter here//RPMunsigned long HtimeA;unsigned long LtimeA;unsigned long HtimeB;unsigned long LtimeB;unsigned long HtimeC;unsigned long LtimeC;unsigned long TtimeA;unsigned long TtimeB;unsigned long TtimeC;unsigned Ttime;double frequency;unsigned long int RPM;int KV;//Voltageconst float arduinoVCC = 4.96;//Your Arduino voltageunsigned long ValueR1 = 11450;unsigned long ValueR2 = 14990;const int analogPin = A1;//the pin connecting the voltage.const int inputResolution =1023;//10 bit boardconst float average_of_V = 20;float voltage;void setup() {pinMode(2,INPUT);//RPMpinMode(3,INPUT);//RPMpinMode(4,INPUT);//RPMSerial.begin(115200);// initialize serial monitorSerial.println();lcd.begin(16,2);lcd.clear();lcd.setCursor(0,0);lcd.print("WMH Racing");}void loop() {readRPM();readVoltage();readCurrent();if (millis() - prevDisplayMillis >= displayInterval) {prevDisplayMillis += displayInterval;showData();}}void readVoltage(){int A1Value = analogRead(analogPin);float voltage_sensed = A1Value * (arduinoVCC / (float)inputResolution);voltage = (voltage_sensed * (ValueR1 + ValueR2)/ValueR2 );} // End readVoltage()void readCurrent(){const float FACTOR = SENS; //float voltageI;// internal variable for voltagefloat voltage_raw = (5.0 / 1023.0) * analogRead(VIN);// Read the voltage from sensorvoltageI = voltage_raw - QOV;_outVoltage = voltageI;_current = abs(voltageI / FACTOR) - offset;} // End readCurrent()void readRPM(){HtimeA = pulseIn(2,HIGH); //read high timeLtimeA = pulseIn(2,LOW); //read low timeHtimeB = pulseIn(3,HIGH);LtimeB = pulseIn(3,LOW);HtimeC = pulseIn(4,HIGH);LtimeC = pulseIn(4,LOW);TtimeA = (HtimeA+LtimeA);TtimeB = (HtimeB+LtimeB);TtimeC = (HtimeC+LtimeC);Ttime = ((TtimeA)+(TtimeB)+(TtimeC))/3;frequency = (1000000.00/Ttime); //getting frequency with Ttime is in Micro secondsRPM = frequency*60;KV = RPM / voltage;}float getVoltageAverage(){float voltage_temp_average=0;for(int f=0; f < average_of_V; f++){readVoltage();voltage_temp_average +=voltage;}return voltage_temp_average / average_of_V;} // End getVoltageAveragefloat getCurrentAverage(){float sum =0, average;for(int c=0; c < average_of_I; c++){readCurrent();sum += _current;}return sum / average_of_I;} // End getCurrentAveragevoid showData() {Serial.println();Serial.print(getVoltageAverage(),2);Serial.print(" V ");Serial.print(getCurrentAverage(),1);Serial.print (" Amps");Serial.print(" RPM ");Serial.print(RPM);Serial.print(" ");Serial.print(frequency);Serial.print(" Hz");lcd.clear();lcd.setCursor(0,0);lcd.print(RPM);lcd.print(" RPM");lcd.setCursor(10,0);lcd.print(getVoltageAverage(),2);lcd.setCursor(14,0);lcd.print(" V");lcd.setCursor(0,1);if ((getCurrentAverage()) < 0.2) {lcd.print("0.0");} else{lcd.print(getCurrentAverage(),1);}lcd.print (" A ");lcd.setCursor(9,1);lcd.print(KV);lcd.print(" KV");}

author: m k
author_rank: Super Contributor
timestamp: August 07, 2023, 06:54:07 pm
content: Enough is an adjective, so you can fix it to be enough.How that then fits is another thing.Serial is a bottleneck.First your 115200 baud is much, then RPM start rising and max chars start dropping, 10k RPM is around 70 chars per round.One other thing is that Arduino libraries are not very optimal, so better drop floating points also, just multiplying is one way, like 4960 mV.I guess it's also acceptable to say that with Arduino you lose some functionality every time.Speed of 1000Hz is not a problem.Maybe two controllers would be easier.There first one would be sort of a hardware controller, then second one would be sort of a high level thing and do the reporting.Ten bits could be the data exchange, eight for actual data, one for strobe and one for first byte.Received can go to ring buffer, so newest rounds are always present.ESP32 speed is easily ten fold compared to 328, raw speed can be more than 30x but there are also easily many other things, so maybe less is more accurate.Is it better, probably, but keep in mind that it's still "just" MHz and every instruction must be processed.The code,do externals for the moment in one block.ReadStuff() {A1Value = analogRead(analogPin);// maybe a small delayTempVIN = analogRead(VIN);// others}

author: trilerian
author_rank: Regular Contributor
timestamp: August 09, 2023, 03:01:09 am
content: Interesting observation I found tonight. I was looking at the raw signals on my scope and decided to look at the virtual neutral created by combining the phases together with resistors. I kind of assumed this would be close to a ground signal since this is the signal I am using for the comparator non inverting side. Well, this signal isn't stable at all, lol. This may be the source of the problem, but where do I go from here. Attached are a couple of pics.Channel 1 at the top is the virtual commonChannel 2 in the middle is an hall sensor for triggering onChannel 3 at the bottom is one of the phase bemfNotice how at full duty the virtual common looks like a good square wave? Not sure what it means, but I can't imagine that signal is easy for the comparator, lol.

author: m k
author_rank: Super Contributor
timestamp: August 09, 2023, 06:13:24 pm
content: Seems that one is up more than two are down, so shapes are not symmetric.