I have a simple C program:
#include <stdint.h>
#define WHEELS_PWM_CYCLES_PER_MS (5)
#define WHEELS_TIME_TO_GO_1_CM_MS (10)
int main(void)
{
for (;;) {
uint32_t x = (uint32_t)WHEELS_TIME_TO_GO_1_CM_MS * 100 * (uint32_t)WHEELS_PWM_CYCLES_PER_MS * 100 / 50;
}
return 0;
}
Originally this is a bigger file, but I reduced it in order to find the error, but without success. I had a program that controlled a dc motor with PWM pulses, but I noticed a strange behavior, so I debugged it with simavr and avr-gdb.
I compiled it with
avr-gcc -c -Wall -Wextra -g -O0 -DF_CPU=8000000UL -mmcu=atmega328p testwheel.c -o testwheel.o
Then I debugged it and got this
Breakpoint 1, main () at testwheel.c:10
10 uint32_t x = (uint32_t)WHEELS_TIME_TO_GO_1_CM_MS * 100 * (uint32_t)WHEELS_PWM_CYCLES_PER_MS * 100 / 50;
(gdb) n
11 }
(gdb) p x
$1 = 16
But x should be 10000, not 16! After some fiddling and experimentation with the compiler flags, I got the right result if I use -ggdb3 instead of -g:
avr-gcc -c -Wall -Wextra -ggdb3 -O0 -DF_CPU=8000000UL -mmcu=atmega328p testwheel.c -o testwheel.o
Breakpoint 1, main () at testwheel.c:10
10 uint32_t x = (uint32_t)WHEELS_TIME_TO_GO_1_CM_MS * 100 * (uint32_t)WHEELS_PWM_CYCLES_PER_MS * 100 / 50;
(gdb) n
11 }
(gdb) p x
$1 = 10000
I have no idea what's wrong with my code and why -ggdb3 fixes it. Does anyone know? Also I cannot use any -g flag in the real atmega, so I need to know what is wrong.
UPDATE:
So I will post the same bug in the real code here, where x is actually used in a meaningful way. The same error is evident there. So please, if someone that really understands what this is about can answer, I would be very happy.
wheels.c
#include "wheels.h"
#include "globals.h"
#include <stdint.h>
#define __DELAY_BACKWARD_COMPATIBLE__
#include <util/delay.h>
#include <avr/io.h>
/*
Q1 Q3
Q2 Q4
If Q1, Q4 is on time, there are 3 types of off times:
- Dynamic braking (Q1 on, Q2, Q3, Q4 off)
- Regenerative braking (Q2, Q3 on, Q1, Q4 off)
- Coasting (all off)
Maximum raise time is 250ns, which is 2 clock cycles at 8MHz.
*/
uint8_t volatile stop_wheels = 0;
static uint8_t speed = 50; // 0-100
void wheels_init() {
DDRD = 0xFF;
PORTD = 0;
}
void wheels_pwd(uint8_t drivepins, uint8_t brakepins, uint32_t cycles) {
PORTD = 0;
uint8_t time_on_us = WHEELS_PWM_CYCLE_US * speed / 100;
uint8_t time_off_us = WHEELS_PWM_CYCLE_US - time_on_us;
//if (time_on_us != 100 ) return;
_delay_us(1); // > maximum raise time
for (uint32_t i = 0; i < cycles; ++i) {
// Using coasting, there is no risk of shoot throughs.
// Even if reversing to opposite direction, this function will always start at PORTD = 0
// and the time it will take to reach this line again is more than 2 clock cycles (maximum raise time).
PORTD = drivepins;
_delay_us(time_on_us);
PORTD = 0;
_delay_us(time_off_us);
if ( stop_wheels ) {
PORTD = brakepins;
stop_wheels = 0;
_delay_ms(800); // Based on dc0.py model 0.6s
break;
}
}
}
void wheels_set_speed(uint8_t spd) {
if ( spd >= WHEELS_MIN_SPEED || spd <= 100 - WHEELS_MIN_SPEED ) {
speed = spd;
}
}
void wheels_go_forward_cm(uint16_t dist) {
wheels_pwd(WHEELS_LEFT_FORWARD | WHEELS_RIGHT_FORWARD,
WHEELS_LEFT_FORWARD_BRAKE | WHEELS_RIGHT_FORWARD_BRAKE,
(uint32_t)WHEELS_TIME_TO_GO_1_CM_MS * dist * (uint32_t)WHEELS_PWM_CYCLES_PER_MS * 100 / speed);
}
void wheels_go_backward_cm(uint16_t dist) {
wheels_pwd(WHEELS_LEFT_BACKWARD | WHEELS_RIGHT_BACKWARD,
WHEELS_LEFT_BACKWARD_BRAKE | WHEELS_RIGHT_BACKWARD_BRAKE,
(uint32_t)WHEELS_TIME_TO_GO_1_CM_MS * dist * (uint32_t)WHEELS_PWM_CYCLES_PER_MS * 100 / speed);
}
void wheels_turn_left_degrees(uint16_t degrees) {
wheels_pwd(WHEELS_LEFT_BACKWARD | WHEELS_RIGHT_FORWARD,
WHEELS_LEFT_BACKWARD_BRAKE | WHEELS_RIGHT_FORWARD_BRAKE,
(uint32_t)WHEELS_TIME_TO_TURN_1_DEGREE_MS * degrees * (uint32_t)WHEELS_PWM_CYCLES_PER_MS * 100 / speed);
}
void wheels_turn_right_degrees(uint16_t degrees) {
wheels_pwd(WHEELS_LEFT_FORWARD | WHEELS_RIGHT_BACKWARD,
WHEELS_LEFT_FORWARD_BRAKE | WHEELS_RIGHT_BACKWARD_BRAKE,
(uint32_t)WHEELS_TIME_TO_TURN_1_DEGREE_MS * degrees * (uint32_t)WHEELS_PWM_CYCLES_PER_MS * 100 / speed);
}
wheels.h
#ifndef WHEELS_H
#define WHEELS_H
#include <stdint.h>
#define WHEELS_MIN_SPEED 1 // Speed = [WHEELS_MIN_SPEED, 1 - WHEELS_MIN_SPEED]
#define WHEELS_PWM_CYCLE_US 200 // Time per cycle, us. If > 255, need to change to uint16_t in wheels_set_speed.
#define WHEELS_PWM_CYCLES_PER_MS (5) // Cycles per ms,typically ranges from 1 kHz to 20 kHz for DC motors.
#define WHEELS_RIGHT_FORWARD 0x03 // 00000011
#define WHEELS_RIGHT_BACKWARD 0x0c // 00001100
#define WHEELS_LEFT_FORWARD 0x30 // 00110000
#define WHEELS_LEFT_BACKWARD 0xc0 // 11000000
#define WHEELS_RIGHT_FORWARD_BRAKE 0x2 // 00000010
#define WHEELS_RIGHT_BACKWARD_BRAKE 0x8 // 00001000
#define WHEELS_LEFT_FORWARD_BRAKE 0x20 // 00100000
#define WHEELS_LEFT_BACKWARD_BRAKE 0x80 // 10000000
#define WHEELS_TIME_TO_TURN_1_DEGREE_MS 0.6 // 1 / ( 360 * 280 RPM / 60000 ms )
#define WHEELS_TIME_TO_GO_1_CM_MS (10) // 1 / ( 280 RPM * 6.7 cm * pi / 60000 ms )
void wheels_init();
void wheels_set_speed(uint8_t spd);
void wheels_go_forward_cm(uint16_t dist);
void wheels_go_backward_cm(uint16_t dist);
void wheels_turn_left_degrees(uint16_t degrees);
void wheels_turn_right_degrees(uint16_t degrees);
#endif
main.c
#include "wheels.h"
int main() {
wheels_init();
for (;;) {
wheels_go_forward_cm(100);
wheels_go_backward_cm(100);
}
}
See the 3rd argument to wheels_go_forward_cm:
(uint32_t)WHEELS_TIME_TO_GO_1_CM_MS * dist * (uint32_t)WHEELS_PWM_CYCLES_PER_MS * 100 / speed
This gives exactly the same error!
volatile uint32_t y = x;under the line and put the breakpoint there. Alternatively debug by single stepping on the assembler instruction level.