I am trying to get a rotary encoder to control the speed of a 7 segment display counting from 0-9 with the Atmel (ATmega328P Xplained mini) microprocessor. My problem is that whenever I run the program the display just counts faster and faster until you can just see an "8", sometimes it seems that I can keep the speed down by turning the rotary encoder CCW and sometimes no effect at all. As I am not that experienced in programming and especially not this stuff I hope someone is capable and willing to help.
Here is my code:
#include <avr/io.h>
void Display (uint8_t x)
{
static uint8_t tabel[] =
{0b11000000,0b11111001,0b10100100,0b10110000,0b10011001,0b10010010,0b10000010,0b11111000,0b10000000,0b10010000};
PORTD = tabel[x];
}
int GetInput (void)
{
uint8_t x = PINC&1;
uint8_t y = (PINC>>1)&1;
if (x == 0 && y == 0) {return 0; }
else if (x == 1 && y == 0) {return 1;}
else if (x == 0 && y == 1) {return 2;}
else {return 3;}
}
int main(void)
{
DDRD = 0xFF; // set PortD as an output
DDRC = 0x00; // set PortC as an input
PORTB = 0x03; // Activate Pull-up resistors
float d = 9000;
int tick = 0;
int i = 0;
int input, state = 0; // initial state
int oldInput = 0;
while (1)
{
input = GetInput();
if (oldInput == 0 && input == 1)
{
d = (d * 1.1);
//slower
}else if (oldInput == 0 && input == 2)
{
d = (d * 0.9);
//faster
}else if (oldInput == 1 && input == 0)
{
d = (d * 0.9);
//faster
}else if (oldInput == 1 && input == 3)
{
d = (d * 1.1);
//slower
}else if (oldInput == 2 && input == 0)
{
d = (d * 1.1);
//slower
}else if (oldInput == 2 && input == 3)
{
d = (d * 0.9);
//faster
}else if (oldInput == 3 && input == 1)
{
d = (d * 0.9);
//faster
}else if (oldInput == 3 && input == 2)
{
d = (d * 1.1);
//slower
}
oldInput = input;
switch (state)
{
case 0: //ini
Display(0);
state = 1;
break;
case 1: //count
if (i == 9)
{
i = 0;
Display(i);
}
else
{
i++;
Display(i);
}
state = 2;
break;
case 2: // delay
if (tick < d)
{
state = 2;
tick++;
}
else
{
state = 1;
tick = 0;
}
break;
case 3: //reset / destroy
break;
}
}
}
First try changing the GetInput function to return a more useful value. Note that bit 0 and bit 1 of PINC already combine to form the integer that you're reconstructing.
int GetInput (void)
{
// array to convert grey scale bit patterns to direction indicators.
// Rows indexed by lastValue, columns indexed by thisValue, and the
// content is -1 for CCW, +1 for CW, 0 for no motion. Note that 0 is
// also used for an invalid transition (2 bits changed at once), but a
// different value could be used for fault detection.
static const int tableGreyToDirection[4][4] =
{
0 , -1, 1 , 0 , // lastValue==0
1 , 0 , 0 , -1, // lastValue==1
-1, 0 , 0 , 1 , // lastValue==2
0 , 1 , -1, 0 // lastValue==3
};
static uint8_t lastValue = 0; // A valid default starting value
uint8_t thisValue = (PINC & 0b00000011); // Use the bottom two bits as a value from 0..3
int result = tableGreyToDirection[lastValue][thisValue];
lastValue = thisValue;
return result;
}
You can then simplify the test in the loop greatly.
while (1)
{
// Check the direction of the encoder: -1 = CCW, +1 = CW, anything else = no motion.
input = GetInput();
if(0 < input)
{
// Motion is CW, so increment the delay (within reasonable bounds).
if(8900 > d) d += 100;
}
else if(0 > input)
{
// Motion is CCW, so decrement the delay (within reasonable bounds).
if(100 < d) d -= 100;
}
// Keep the rest as it is...
}
It would be advisable to change d to be a uint16_t and tidy it up a little. Further tips include using #define to provide readable names for constants. E.g. in my table of directions you could use:
#define ENCODER_CW 1
#define ENCODER_CCW -1
#define ENCODER_NEITHER 0
...
static const int tableGreyToDirection[4][4] =
{
ENCODER_NEITHER, ENCODER_CCW, ENCODER_CW, ENCODER_NEITHER, // lastValue==0
...
I'm sure you can fill it out yourself.
I checked your SW, but I can't find big issue instantly.
You'd better check below part.
If you didn't touch the encoder but speed is faster and faster
: do you have a scope to check the encoder input port whether noise is input from the port or not.
If two input port is stable, please check your value also stable
: old input and new input value should be same
: check by log or output toggle unused port when the value is changed. you can debug your own code.
You'd better add amount tick value than multiply directly to prevent d value becomes 0.
your CPU has to run as fast as detect port status change in main loop. - I think it is possible if this code is all of your system.
Related
I have made a (pretty bad) line follower.
Here is a sketch to roughly know the shape of the robot and location of the treads and sensors
[-] 0 0 [-] // 0 = color sensor
[-]------[-] // - = robot body
[-]------[-] // [-] = tank tread
[-] [-]
Here's what it does:
get Red, Green & Blue, make average of sensor 1 readings, do the same for 2
subtract to get value
this value will go through the PID part
steer with calculated steering
repeat (all of this is in a loop)
I use RGB and not reflected intensity (which is what is commonly used), because sometimes I need to detect if there's green color under the sensor (if there is, turn).
The real problem comes with the steering part. Unfortunately, it only accelerates a motor, meaning that in very tight turns we just lose the line.
Optimally, it should compensate a bit with the other motor (maybe going in the other direction?), but I am not sure how to calculate the speed of the motor, nor how to enforce this very strict line following policy.
Here is the code (I am also very grateful for any kind of tips on how to clean up the code! This is my first project in C :D ). I am not asking to read it all (it is pretty long), you could also just look at the steering function, and work your way back to rawFollowLine, this should hopefully shorten the code.
void rawFollowLine(int speed, float Kp, float Ki, float Kd){
_checkInit();
set_sensor_mode(sn_lx_color, "RGB-RAW");
set_sensor_mode(sn_rx_color, "RGB-RAW");
//printAllSensors();
int wasBlackCounter = 0;
int wasBlack = 0;
int lastBlack = 0;
for (int i = 0; i < 2000; i++)
{
if (isTerminating == 1)
{
killMotors(0);
break;
}
int greenCheck = rawGreenCheck(&wasBlack, &wasBlackCounter, &lastBlack);
if (wasBlack == 1){
wasBlackCounter++;
if (wasBlackCounter > 50){
wasBlackCounter = 0;
wasBlack = 0;
}
}
if (greenCheck == 1)
{
// lx is green
killMotors(1);
usleep(400 * 1000);
drive(200, 70);
waitIfMotorIsRunning();
killMotors(1);
pivotTurn(-90);
}
else if (greenCheck == 2)
{
// rx is green
killMotors(1);
usleep(400 * 1000);
drive(200, 70);
waitIfMotorIsRunning();
killMotors(1);
pivotTurn(90);
}
else if (greenCheck == 3)
{
// both rx and lx are green
killMotors(1);
turn(180);
}
else if (greenCheck == 5)
{
if(lastBlack == 2)
{
lastBlack = 0;
drive(100, -200);
//pivotTurn(50);
}
else if (lastBlack == 1)
{
lastBlack = 0;
drive(100, -200);
//pivotTurn(-50);
} else {
pidLineRaw(speed, Kp, Ki, Kd, &lastBlack);
}
}
else
{
pidLineRaw(speed, Kp, Ki, Kd, &lastBlack);
}
}
killMotors(1);
}
int rawGreenCheck(int *wasBlack, int *wasBlackCounter, int *lastBlack)
{
// Some documentation
// return nums:
// 3 = double green
// 2 = right green
// 1 = left green
// 0 = no green
int lx_red;
int lx_green;
int lx_blue;
int rx_red;
int rx_green;
int rx_blue;
get_sensor_value(0, sn_lx_color, &lx_red);
get_sensor_value(0, sn_rx_color, &rx_red);
get_sensor_value(1, sn_lx_color, &lx_green);
get_sensor_value(1, sn_rx_color, &rx_green);
get_sensor_value(2, sn_lx_color, &lx_blue);
get_sensor_value(2, sn_rx_color, &rx_blue);
//printf("rx_red %d\n", rx_red);
rx_red = (rx_red * rx_ratio_r);
rx_green = (rx_green * rx_ratio_g);
rx_blue = (rx_blue * rx_ratio_b);
//printf("rx_red (again) %d\n", rx_red);
if(
lx_red < 55 &&
lx_green > 90 &&
lx_blue < 55 &&
rx_red < 55 &&
rx_green > 90 &&
rx_blue < 55
)
{
// rx and lx see green
if (*wasBlack == 1)
{
// Apparently we crossed an intersection!
printf("Apparently we crossed an intersection!\n");
// We need to go straight.
*wasBlack = 0;
*wasBlackCounter = 0;
return 0;
}
else
{
return 3;
}
}
else if(lx_red < 55 && lx_green > 90 && lx_blue < 55)
{
// lx sees green
return 1;
}
else if(rx_red < 55 && rx_green > 90 && rx_blue < 55)
{
// rx sees green
return 2;
}
else if(rx_red < 50 && rx_green < 50 && rx_blue < 50 && lx_red < 50 && lx_green < 50 && lx_blue < 50)
{
// rx and lx see black
// this is needed if the intersection has the green tiles after the black line
printf("We are on the line? Is this an intersection?\n");
*wasBlack = 1;
return 0;
}
else if(lx_red < 55 && lx_green < 55 && lx_blue < 55)
{
// lx = right sees black
// this is needed if the intersection has the green tiles after the black line
//printf("We are on the line? Is this an intersection?\n");
killMotor(1, motor[R]);
rotateTillBlack(motor[L], sn_rx_color);
//printf("ASS2\n");
return 0;
}
else if(rx_red < 55 && rx_green < 55 && rx_blue < 55)
{
// rx = left sees black
killMotor(1, motor[L]);
rotateTillBlack(motor[R], sn_lx_color);
//printf("ASS1\n");
return 0;
}
//*lx_color_status = 0;
//*rx_color_status = 0;
*lastBlack = 0;
return 0;
}
void pidLineRaw(int speed, float Kp, float Ki, float Kd, int *lastBlack)
{
int red_lx_color;
int red_rx_color;
int green_lx_color;
int green_rx_color;
int blue_lx_color;
int blue_rx_color;
int lx_color;
int rx_color;
int last_error = 0;
int integral = 0;
int derivative = 0;
//float Kp = 0.1;
//float Ki = 0;
//float Kd = 0;
//set_sensor_mode(sn_lx_color, "COL-REFLECT");
//set_sensor_mode(sn_rx_color, "COL-REFLECT");
get_sensor_value(0, sn_lx_color, &red_lx_color);
get_sensor_value(0, sn_rx_color, &red_rx_color);
get_sensor_value(1, sn_lx_color, &green_lx_color);
get_sensor_value(1, sn_rx_color, &green_rx_color);
get_sensor_value(2, sn_lx_color, &blue_lx_color);
get_sensor_value(2, sn_rx_color, &blue_rx_color);
lx_color = (red_lx_color + green_lx_color+ blue_lx_color)/3;
rx_color = ( (red_rx_color*rx_ratio_r) + (green_rx_color*rx_ratio_g) + (blue_rx_color*rx_ratio_b))/3;
if(*lastBlack == 0)
{
int error = lx_color - rx_color;
integral = integral + error;
derivative = error - last_error;
last_error = error;
int steering_val = (error * Kp) + (integral * Ki) + (derivative * Kd);
// printf("error: %d\nsteering: %d\n",error, steering_val);
move_steering(-steering_val, speed, 1, 0);
} else if (*lastBlack == 1)
{
printf("lx_color_status\n");
move_steering(35, speed, 1, 0);
move_steering(-2, speed, 1, 0);
}
else if (*lastBlack == 2)
{
printf("rx_color_status\n");
move_steering(-35, speed, 1, 0);
move_steering(2, speed, 1, 0);
}
else
{
printf("HMMM: %d\n", *lastBlack);
exit(666);
}
}
static void _getSteeringSpeed(int speed, int *lx_speed, int *rx_speed, int steering)
{
if(steering > 100 || steering < -100)
{
printf("Yo wtf steering is %d\n", steering);
}
else
{
int speed_factor = (50 - abs(steering)) / 50;
*lx_speed = speed;
*rx_speed = speed;
if(steering >= 0)
{
*rx_speed = *rx_speed * speed_factor;
}
else
{
*lx_speed = *lx_speed * speed_factor;
}
}
}
Some parts are omitted, yes, they are not required to solve the problem.
I am also extremely sorry as there might be unused variables and such. I am working on refactoring the project, I'll update the post when I'm done.
So, summing everything up, I need to make sure that the steering part properly turns and follows the line. How do I do that? Is the code that I wrote even suitable? I'm guessing the steering itself might need some sort of feedback loop, to check if it's on the line?
Is it possible to call a line number in embedded C that is outside of the function?
Simple goto label commands can't work because I am in the if statement loop: I want to restart the function once the error is greater than 200.
void followWall(void)
{
begin:
int desired = getADC(); //store the first ADC reading as the desired distance from the wall
int right = 200; //pre set all variables to be called in the drive function
int left = 200; //ints are needed here for bit masking later on to stop overflow
char rightH = 0;
char leftH = 0;
float k = 0.5; //constant to times the error to fine tune the response
drive(rightH,right,leftH,left); //drive the robot to the pre set variables
while( bumpFlag == 0 ) //while the robot has not been bumped or encounted a cliff
{
int actual = getADC(); //call a new ADC reading everytime at start of while as the current reading
rightH = 0;
leftH = 0; //set variables back to default to stop constant increase and decrease in variables
left = 200;
right = 200;
char error = abs(actual-desired); //calculate the error by minusing the actual distance to the desired
if(motorAngle < 180) //wall on left between 1st and 2nd quadrant
{
if(error > 200)
{
stop();
moveStepper(33,0);
goto begin;
}
if (actual > desired)
{
right -=((error)*k);
left += ((error)*k);
}
else if (actual < desired)
{
left -=((error)*k);
right +=((error)*k);
}
}
else if (motorAngle > 180)
{
if(error > 200)
{
stop();
moveStepper(33,1);
goto begin;
}
if (actual > desired)
{
left -=((error)*k);
right +=((error)*k);
}
else if (actual < desired)
{
right -=((error)*k);
left +=((error)*k);
}
}
drive(rightH,right,leftH,left); bumpSensor();
setLCDCursor(0x09);
writeLCDNumber(convert(actual)); //constantly write the converted AC value on the LCD
}
stop(); //stop the robot
}
You should restructure your code to avoid a GOTO. Another loop is needed. Something like this.
void followWall(void)
{
repeat = 0;
do // add this loop here
{
...
...
while( bumpFlag == 0 )
{
repeat = 0;
..
..
if(motorAngle < 180)
{
..
if(error > 200)
{
..
..
repeat = 1;
break;
}
}
else if (motorAngle > 180)
{
..
if(error > 200)
{
..
..
repeat = 1;
break;
}
}
}
} while (repeat == 1);
}
I'm a first time programmer trying to complete a simple command line program as part of the first assignment for an online course I am taking, but I seem to have hit a roadblock that I can't figure out with GDB or my own research.
After hours of rewrites, and hours of debugging, I finally got the code below to compile. The program is supposed to take a credit card number as an input, and then check whether it's valid per the specifications of the assignment. I used a test number from here: PayPal Test Credit Cards
The odd thing is, when I enter an AMEX card number, it correctly produces the text "AMEX", but when I try a Visa or a Master Card, it prints "INVALID".
In GDB I broke at the Verify function and it seems to incorrectly skip these two if/else if statements without proceeding to the Checksum function even though conditions appear to be met.
if (firstDigit == 4 && totalDigits == (13 | 16) && Checksum(cardNumber, totalDigits) == 0) // checks for a valid Visa.
...
else if (firstDigit == 5 && secondDigit == (1 | 2 | 3 | 4 | 5) && totalDigits == 16 && Checksum(cardNumber, totalDigits) == 0) // checks for a valid Mastercard.
...
The AMEX line of code that correctly executes is:
else if (firstDigit == 3 && secondDigit == (4 | 7) && totalDigits == 15 && Checksum(cardNumber, totalDigits) == 0) // checks for a valid American Express.
The arguments for all three lines seem to be formatted exactly the same. That is far as I could get in GDB though. I would print totalDigits, firstDigit, and secondDigit in GDB right before stepping through the above two non-executing lines and everything looked correct. So I'm stumped, why is the AMEX line executing, but not the others?
Thanks in advance everyone. This is the first program after hello.c that I've tried to write, so I am open to absolutely any criticism or suggestions if it looks like I'm doing something weird/wrong.
Full code:
checker.c
#include <stdio.h>
#include <stdlib.h>
int MAX = 16;
int* DigitSort(unsigned long long x, int* array);
int Verify(int* array);
int main (void)
{
int* output = malloc (sizeof(int) * (MAX + 2)); // creates a blank array for the individual digits of the card number.
unsigned long long userInput = 0;
do
{
printf("Please enter a credit card number:\n");
scanf("%lld", &userInput);
}
while (userInput <= 0); // checks to make sure the user entered a number.
switch(Verify(DigitSort(userInput, output))) // sorts the user's input into individual digits and verifies the card type and validity.
{
case 1 :
printf("VISA\n");
break;
case 2 :
printf("MASTERCARD\n");
break;
case 3 :
printf("AMEX\n");
break;
case 0 :
printf("INVALID\n");
break;
default :
printf("INVALID\n");
}
free(output);
return 0;
}
int Verify(int* array) // verifies whether or not a card number is valid. Must pass the function a sorted array of individual digits.
{
int* cardNumber = array;
int firstDigit = cardNumber[0];
int secondDigit = cardNumber[1];
int totalDigits = 0;
int Checksum(int* cardNumber, int totalDigits);
int i = 0;
while (firstDigit >= 1 && cardNumber[i] >= 0) // this step counts the number of digits in the array.
{
totalDigits = totalDigits + 1;
i++;
}
if (firstDigit == 4 && totalDigits == (13 | 16) && Checksum(cardNumber, totalDigits) == 0) // checks for a valid Visa.
{
return 1;
}
else if (firstDigit == 5 && secondDigit == (1 | 2 | 3 | 4 | 5) && totalDigits == 16 && Checksum(cardNumber, totalDigits) == 0) // checks for a valid Mastercard.
{
return 2;
}
else if (firstDigit == 3 && secondDigit == (4 | 7) && totalDigits == 15 && Checksum(cardNumber, totalDigits) == 0) // checks for a valid American Express.
{
return 3;
}
else // if the card number doesn't match any of the above conditions or fails the checksum, an 'I' for Invalid is returned.
{
return 0;
}
}
int* DigitSort(unsigned long long x, int* array) // takes a long long as input and sorts it into individual digits
{
int* arrayReversed = malloc (sizeof(int) * (MAX + 2)); // creates a new array to hold the reversed order of digits.
int i = 0;
arrayReversed[0] = 0;
if (i < (MAX - 1) && x >= 10)
{
do
{
arrayReversed[i] = x % 10;
x = x / 10;
i++;
}
while (i < (MAX -1) && x >= 10);
}
if (i < MAX && x >= 1 && x <= 9)
{
arrayReversed[i] = (int) x;
x = (x - x);
}
if (x == 0)
{
int j = 0;
do
{
array[j] = arrayReversed[i]; // sorts the digits from the reversed array and places them into the sorted array.
j++;
i--;
}
while (j < MAX && i >= 0);
array[j] = -1;
}
free(arrayReversed);
return array;
}
int Checksum(int* cardNumber, int totalDigits)
{
int sum1 = 0;
int sum2 = 0;
int i = (totalDigits - 2);
int j = (totalDigits - 1);
while (i >= 0)
{
sum1 = ((cardNumber[i] * 2)%10) + ((cardNumber[i] * 2)/10) + sum1;
i -= 2;
}
while (j >= 0)
{
sum2 = (cardNumber[j] + sum2);
j -= 2;
}
if (((sum1 + sum2) % 10) == 0)
{
return 0;
}
else
{
return 1;
}
}
Your first problem is here:
if (firstDigit == 4 && totalDigits == (13 | 16) && ...
You need to write:
if (firstDigit == 4 && (totalDigits == 13 || totalDigits == 16) && ...
Your first check is looking for 0x1D == 29 as the number of digits (because, as paisanco points out in a comment, the | operator is the bitwise OR operator), and no credit card needs 29 digits (yet, and not for a long time to come). Note the extra parentheses for clarity and accuracy. Don't mess around risking removing them — the code won't work properly again. And in general, be explicit if your condition has both && and || operators and use parentheses to group terms explicitly.
You have similar problems elsewhere. As it happens, (4 | 7) is the same value as 7, so the condition works when the second digit is 7 (but not when it is 4). But it doesn't mean what you intended it to mean.
Computer languages don't work the same as human languages. Get used to writing out the condition somewhat more verbosely. Some other languages provide shorthands for these conditions; C is not such a language.
I'm trying to use u8glib on an arduino ethernet, in the first phase i need to display a simple countdown so in a little pretty way. To test if the display can be managed in time.
I have to measure changes in order of milliseconds, likes 30ms, therefore i need that the countdown changes 1 units in about 50/100ms.
This is the oled http://www.seeedstudio.com/wiki/Grove_-_OLED_Display_1.12%22
Now im trying U8glib because has a lot of font integrated and it seems well documented.
But the big problem is: The countdown, without sleep obiuvsly, is too slow! In order of 1 change in 1second.
What i'm wrong? I need to change library?
Thanks
This is the code
#include "U8glib.h"
U8GLIB_SSD1327_96X96_GR u8g(U8G_I2C_OPT_NONE); // I2C
#define MAX_VAL 200
#define MAX_VAL1 250
#define MAX_VAL2 350
#define MAX_VAL3 450
#define MAX_VAL4 500
int current_max_val = MAX_VAL1;
int currentBB = current_max_val;
char bb_n[4] = {0,0,0,0};
int bb_level = 4;
static int last_bb_n = current_max_val;
void draw(void) {
int sizev = u8g.getStrWidth(bb_n);
u8g.drawStr( (48 - (sizev/2)),60,bb_n);
for(int i=0;i<bb_level;i++){
u8g.drawTriangle((i*10)+0,90, (i*10)+10,90, (i*10)+5,85);
}
last_bb_n = currentBB;
u8g.drawHLine(0,20, 96);
u8g.drawHLine(0,70, 96);
}
void setup(void) {
// flip screen, if required
//u8g.setRot180();
// set SPI backup if required
//u8g.setHardwareBackup(u8g_backup_avr_spi);
// assign default color value
if ( u8g.getMode() == U8G_MODE_R3G3B2 ) {
u8g.setColorIndex(255); // white
}
else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) {
u8g.setColorIndex(3); // max intensity
}
else if ( u8g.getMode() == U8G_MODE_BW ) {
u8g.setColorIndex(1); // pixel on
}
else if ( u8g.getMode() == U8G_MODE_HICOLOR ) {
u8g.setHiColorByRGB(255,255,255);
}
u8g.setFont(u8g_font_gdb30);
}
void loop(void) {
// picture loop
u8g.firstPage();
count();
do {
draw();
} while( u8g.nextPage() );
}
void count(){
if(currentBB == 0){
currentBB = current_max_val;
}
itoa(currentBB,bb_n,10);
if(currentBB >= ((current_max_val/4)*3)){
bb_level = 4;
}else if((currentBB < ((current_max_val/4)*3)) && (currentBB >= (current_max_val/4)*2)){
bb_level = 3;
}else if((currentBB < ((current_max_val/4)*2)) && (currentBB >= (current_max_val/4))){
bb_level = 2;
}else if(currentBB < (current_max_val/4)){
bb_level = 1;
}else{
bb_level = 0;
}
currentBB--;
Some ideas for optimization:
Use U8GLIB_SSD1327_96X96_2X_GR
Make sizev a global variable and calculate its value in count()
Oliver
Give a AST tree, I want to generate an assembly-like language. I'm trying using Sethi-Ullman algorithm but I have some questions in the algorithm implemetation.
What should I do when I run out of registers?
currently I do the following:
Emit a push REG where REG is the register of right subtree, evaluate left subtree, get one free register assign as register of right subtree and then emit a POP REG operation where REG is the register of right subtree too.
How should I implement the function to get a free register? currently I'm using an implementation like this instead of a stack-based:
enum Reg { Reg_r0, Reg_r1 };
Reg regs[] = { Reg_r0, Reg_r1 };
Reg getreg() {
static int c;
if(c == sizeof(regs) / sizeof(int))
c = 0;
return regs[c++];
}
Here's a pseudo-code (from C-language) how to implement it from what I unsertood(including the label() function)
// K is the number of registers available
int K = 2;
void gen(AST ast) {
if(ast.left != null && ast.right != null) {
int l = ast.left.n;
int r = ast.right.n;
if(l >= K && r >= K) {
gen(ast.right);
ast.n -= 1;
emit_operation(PUSH, ast.right.reg);
gen(ast.left);
ast.reg = getreg();
emit_operation(POP, ast.right.reg);
} else if(l >= r) {
gen(ast.left);
gen(ast.right);
ast.n -= 1;
} else if(l < r) {
gen(ast.right);
gen(ast.left);
ast.n -= 1;
}
ast.reg = getreg();
Reg r1 = ast.left.reg;
Reg r2 = ast.right.reg;
emit_operation(ast.type, r1, r2);
} else if(ast.type == Type_id || ast.type == Type_number) {
ast.n += 1;
ast.reg = getreg();
emit_load(ast);
} else {
print("gen() error");
// error
}
}
// ershov numbers
void label(AST ast) {
if(ast == null)
return;
label(ast.left);
label(ast.right);
if(ast.type == Type_id || ast.type == Type_number)
ast.n = 1;
// ast has two childrens
else if(ast.left not null && ast.right not null) {
int l = ast.left.n;
int r = ast.right.n;
if(l == r)
ast.n = 1 + l;
else
ast.n = max(1, l, r);
}
// ast has one child
else if(ast.left not null && ast.right is null)
ast.n = ast.left.n;
else
print("label() error!");
}
EDIT: Tell me please if more context is needed to understand this.
Sethi-Ullman is really just a scheduling algorithm, not a register allocation algorithm, so it just tells you the order in which to do operations to minimize the number of registers needed; it does not tell you which registers to use where.
So you need to combine it with a register allocation strategy -- usually just a greedy allocator. Then there's the question of what to do if you run out of registers -- do you insert spills inline, or abort and do something else?
In order to do simple greedy allocation inline with your scheduling and instruction generation (what you seem to be doing with your simple gen recursive procedure), you'll need to keep track of which registers are in use at any given time. The easiest way is by adding an extra in_use argument to your gen function:
typedef unsigned RegSet; /* using a simple bitmask for a set -- assuming that
* unsigned is big enough to have a bit per register */
void gen(AST *ast, RegSet in_use) {
if(ast->left != 0 && ast->right != 0) {
if (ast->left->n >= ast->right->n) {
gen(ast->left, in_use);
gen(ast->right, in_use | (1 << ast->left->reg));
} else {
gen(ast->right, in_use);
gen(ast->left, in_use | (1 << ast->right->reg)); }
ast->reg = ast->left->reg
emit_operation(ast->type, ast->left->reg, ast->right->reg);
} else if(ast->type == Type_id || ast->type == Type_number) {
ast->reg = pick_unused_register(in_use);
emit_load(ast);
} else ....
Note that you need a separate recursive pass to calculate n for each node (Sethi-Ullman inherently requires two traversals, with the first traversal computing bottom up the n value for the second traversal to use top-down).
Now the above code doesn't deal with running out of registers at all. To do that, you need to insert some spills. One way is to detect that all registers are in use before making a recursive call and spill then, restoring afterwards:
void gen(AST *ast, RegSet in_use) {
if(ast->left != 0 && ast->right != 0) {
Reg spill = NoRegister; /* no spill yet */
AST *do1st, *do2nd; /* what order to generate children */
if (ast->left->n >= ast->right->n) {
do1st = ast->left;
do2nd = ast->right;
} else {
do1st = ast->right;
do2nd = ast->left; }
gen(do1st, in_use);
in_use |= 1 << do1st->reg;
if (all_used(in_use)) {
spill = pick_register_other_than(do1st->reg);
in_use &= ~(1 << spill);
emit_operation(PUSH, spill); }
gen(do2nd, in_use);
ast->reg = ast->left->reg
emit_operation(ast->type, ast->left->reg, ast->right->reg);
if (spill != NoRegister)
emit_operation(POP, spill);
} else ...
Of course, this turns out to be not terribly efficient -- its usually better to spill sooner and refill later, but only when you know you're going to run out of registers.