I'm working on a project of differential drive control in which I use speed encoders for the odometry.
I count the number of rising edges using counter0
and counter5
(on Arduino Mega 2560
), which works fine by itself, but when I combine that code with other parts of my code, many things stop working and I can't figure out why. My current guess is that it is related to timer0
.
So my question is: what can go wrong when we modify the registers for timer0? I know that delay()
, millis()
, micros()
, and PWM on pins 4
and 13
won't be available anymore, but are there other things that I should be aware of? Are there some libraries that stop working?
If that is the case, I'm thinking of using hardware interrupts for the odometry.
Thanks in advance.
Edit: Here's more information about the project:
In the project, I use a behavior based controller to build a differential drive robot that goes to some goal coordinates while avoiding obstacles.
The speed measurement is done using speed encoders and optocouplers. those values are also used for updating the position and heading of the robot. It runs every 210 milliseconds.
The obstacle distance is measured using 7 ultrasonic sensors that are controlled using pin change interrupts.
The only external libraries that I use are TimerOne.h
and TimerThree.h
(which do not use timer0
) and I made sure not to use timer0
(at least directly) in the parts that I wrote myself.
My code is interrupt-driven, so I wonder if that could also be a cause for the wrong behavior.
Here's the main code
#include "definitions.h" // contains constants that should be accessible by many files
#include "diff_cntrl.h" // controls the wheels (left and right speed) uses timer0 and timer5 for speed measurement
#include <TimerThree.h> // used for generating timer interrupts for the differential control
#include "A7.h" // used for commmunication using an A7 module through Serial1
#include "M_Ultrasonics.h" // measures distance from 7 ultrasonic sensors using pin change interrupts on A8 - A14 (PORTK) and TimerOne.h
#include "FuzzyBBC.h" // a fuzzy logic behavior-based controller (does only floating point computations
volatile double dists[NUM_US]; // array where the obstacle distances will be stored
volatile uint8_t done = 0; // flag set by M_Ultrasonics.h when all 7 seven distances are measured
FuzzyBBC fbbc; // reference to the behavior based controller
volatile double xG = 0, yG = 0; // coordinates of the goal
volatile double v = 0, w = 0; // desired velocity and angular velocity. set by fbbc and used by the differential controller
// variables holding the current state of the
volatile double x = 0, y = 0, phi = 0; // coordinates and heading
volatile double dx, dy, dphi; // changes in coordinates and heading (used for debugging
// variables used for the speed control of the motors: reference speed, actual speed, counter value, output voltage for left and right motors
volatile double l_ref = 0, l_spd = 0, r_ref = 0, r_spd = 0;
volatile uint8_t l_ctr = 0, r_ctr = 0;
volatile uint8_t l_volt = 0, r_volt = 0;
volatile boolean newvolt = true; // flag set when a new voltage value is computed
volatile boolean updated = false; // flag set to allow transmission of data to the remote machine
volatile uint8_t update_count = 0;
//------------debug variables-----------------
volatile double dvl, dvr;
//--------------------------------------------
void setup()
{
delay(5000);
A7begin(); // starts up the A7 module and connects to a predefined remote machine
initMotors(); // initialize the pins for the motor control (I use an LM293D motor driver)
setup_cntr(); // set the registers for timer0 and timer5
// vw2lr();
fbbc.begin(&xG, &yG, NUM_US, dists, &x, &y, &phi, &v, &w); // starting the fuzzy logic controller, it has direct access to the provided variables
HCRS04.begin(T_US, dists, &done); // starting the ultrasonic sensors controller (it runs every 30 millisecond
Timer3.initialize(T_MOTOR*1000L); // timer interrupts for the speed control (T_MOTOR = 210 milliseconds)
Timer3.attachInterrupt(cntrISR);
}
void loop() {
if(done) // wait for obstacle distances to be updated
{
arbiter(); // update v and w
if(updated && !waiting){ // send data to the remote machine
String info = "v: " + String(v) + "\tw: " + String(w) +
"\t\tvl: " + String(l_spd) + "\tlref: " + String(l_ref) + "\tlvolt: " + String(l_volt) +
"\t\tvr: " + String(r_spd) + "\trref: " + String(r_ref) + "\trvolt: " + String(r_volt) +
"\t\tx: " + String(x) + "\ty: " + String(y) + "\txG: " + String(xG) + "\tyG: " + String(yG);
LogInfo(info);
updated = false;
}
done = false;
}
if (A7_STREAM.available() > 0) { // receive input from the A7 module (Serial1)
String msg = "";
if(waiting){
get_A7_resp(); // read the message (function in A7.h)
msg = resp; // save the message (variable in A7.h)
resp = "";
}
else{
msg = readStr();
}
if (msg.length() > 2) // process the received message
{
if (msg.substring(0, 2).equals("xy")) {
readXYG(msg);
resp = "";
}
else if(msg.equals("stop")){
xG = x;
yG = y;
resp = "";
}
else if(msg.equals("+TCPCLOSE:0")){
disconnected();
}
}
}
}
inline void arbiter(){
fbbc.arbiter();
newvolt = true;
vw2lr(); // convert v and w to left speed and right speed
}
inline void cntrISR() // ISR that runs every 210 milliseconds (for speed control)
{
// compute the speed of the car
read_rm();
read_lm();
ctr2lr(); // convert counter values to centimeter/second
if (newvolt){
spd2volt_init();
newvolt = false;
}
else
spd2volt();
// update motor values
analogWrite(L_PWM_PIN, l_volt);
analogWrite(R_PWM_PIN, r_volt);
updatePos();
}
inline void updatePos() // compute new position (using differential drive kinematic equations)
{
lr2xyphi();
}
inline void readXYG(String cmd) // get the new goal coordinates
{
int sp1 = cmd.indexOf(' ');
int sp2 = cmd.indexOf(' ', sp1 + 1);
xG = (double)cmd.substring(sp1 + 1, sp2).toInt();
yG = (double)cmd.substring(sp2 + 1, cmd.indexOf('~',sp2+1)).toInt();
}
the motor initialization code:
inline void setup_cntr()
{
pinMode(CTR0_PIN, INPUT_PULLUP);
pinMode(CTR5_PIN, INPUT_PULLUP);
TCNT5H = 0;
TCNT5L = 0;
TCCR5B = 0X07;
TCNT0 = 0;
TCCR0B = 0X07;
}
inline void initMotors(){
pinMode(L_FWD_PIN, OUTPUT);
pinMode(L_BACK_PIN, OUTPUT);
pinMode(L_PWM_PIN, OUTPUT);
pinMode(L_EN_PIN, OUTPUT);
digitalWrite(L_EN_PIN, HIGH);
digitalWrite(L_FWD_PIN, HIGH);
digitalWrite(L_BACK_PIN, LOW);
pinMode(R_FWD_PIN, OUTPUT);
pinMode(R_BACK_PIN, OUTPUT);
pinMode(R_PWM_PIN, OUTPUT);
pinMode(R_EN_PIN, OUTPUT);
digitalWrite(R_EN_PIN, HIGH);
digitalWrite(R_FWD_PIN, HIGH);
digitalWrite(R_BACK_PIN, LOW);
}
ultrasonic sensors code:
#include "M_Ultrasonics.h"
M_Ultrasonics HCRS04;
void measISR();
void trigISR();
M_Ultrasonics::M_Ultrasonics()
{
pinIndex = -1;
for(int i=0; i < NUM_US; i++)
{
pinMode(trigs[i],OUTPUT); // trigs: array containing the trigger pins of the ultrasonic sensors
digitalWrite(trigs[i],LOW);
}
}
void M_Ultrasonics::begin(uint32_t period, volatile double *dists_, volatile uint8_t *done_)
{
Timer1.initialize(period*1000);
Timer1.attachInterrupt(trigISR);
dists = dists_;
done = done_;
pinIndex = -1;
for(int i=0; i < NUM_US; i++)
{
pinMode(trigs[i],OUTPUT);
digitalWrite(trigs[i],LOW);
dists[i] = MAX_DIST;
}
enable_pci();
}
void M_Ultrasonics::end()
{
Timer1.stop();
Timer1.detachInterrupt();
PCICR = 0;
}
inline void M_Ultrasonics::trig() // send the trigger signal to the sensor
{
pinIndex ++;
if(pinIndex == NUM_US)
{
pinIndex = 0;
*done = 1;
}
set_pci();
digitalWrite(trigs[pinIndex], HIGH);
delayMicroseconds(ping);
digitalWrite(trigs[pinIndex], LOW);
}
inline void M_Ultrasonics::enable_pci() // enable pin change interrupts on PORTK
{
PCICR = 0x04;
}
inline void M_Ultrasonics::set_pci() // activate interrupts for the current pin
{
PCMSK2 = (1<<pinIndex);
}
inline unsigned long M_Ultrasonics::read_time() // convert the timer value to microseconds
{
return (/*(TCNT1H<<8) | */TCNT1L) * F_CLOCK;
}
inline void M_Ultrasonics::meas()
{
if(PINK & (1<<pinIndex)) { // check if the pin is high
s_echo = /*micros();/*/read_time();
dists[pinIndex] = MAX_DIST*DOBST_COEFF;
}
else
{
f_echo =/* micros();/*/read_time();
// d = (f_echo-s_echo)*soundSpeed/2E4;
if(d < MAX_DIST) {
dists[pinIndex] = d/**DOBST_COEFF*/;
}
// dists[pinIndex] = d; // (double)(f_echo - s_echo);
}
}
ISR (PCINT2_vect) // handle pin change interrupt for A8 to A14 here
{
measISR();
}
void measISR() // ISR called on rising or falling edge on the current pin
{
HCRS04.meas();
}
void trigISR() // function called every 30 millisecond to trigger the next ultrasonic sensor
{
HCRS04.trig();
}
1 Answer 1
Many libraries expect delay(), millis() or micros() to work.
-
2I copied Edgar Bonet's comment here to make a real answer, because Stack Exchange throws up old questions with no answers in the hope that someone will answer them. This is why you answer as an answer and not as a comment.2024年08月10日 08:19:27 +00:00Commented Aug 10, 2024 at 8:19
-
Half the time I make an answer somebody says it should have been a comment. I'm starting to think that there's not a "right" way to use this site that won't be complained about by someone.Delta_G– Delta_G2024年08月10日 15:35:24 +00:00Commented Aug 10, 2024 at 15:35
-
@Delta_G The policy is clearly stated in the help center, that is the definite reference how to do it, not what random people say. Answers are answers, comment are just comments.the busybee– the busybee2024年08月10日 18:51:12 +00:00Commented Aug 10, 2024 at 18:51
-
OP states in the question that they don't use any libraries which would use these functions. so this answer was already in the question. that is why this was only a comment.2024年08月16日 04:16:04 +00:00Commented Aug 16, 2024 at 4:16
delay()
,millis()
ormicros()
to work.