RobotC programming tips

Documentation, that’s a novel concept :slight_smile: The help files are so out of date it’s not funny. Anyway, I’m using semaphores and they work as far as I can tell. There are only three functions, I can write up an explanation sometime.

This will probably take a number of posts, I guess it’s either you magicode or I as we are the only ones contributing code to this thread. Let me have a think and try and come up with a simple but worthwhile example.

1 Like

I was trying to come up with a good relevant example for multitasking as well. It seems that the type of things that multitasking should really be used for are too complicated to demonstrate in a beginners tutorial. This is a good place to start though: http://robotc.net/forums/viewtopic.php?f=11&t=3341

Hey there,
I’m pretty much a novice to RobotC but I am trying to learn how to get things done and would appreciate if someone would share information on ways to use the emulator. I found yesterday that you can select PC emulator as a target download. and you can get the 16 line LCD display up for feedback on what the code would look like on your robot as well as the regular debugger windows. I see there seems to be a way to attach a joystick to your pc. the one in the help picture is not a vex but possibly a x box 360 controller. does anyone know how to go about using a joystick with the emulator and if the emulator can be configured to provide some sort of sensor feedback? even if you can’t use sensors it would be a very valuable tool to test logic flows.

Cheers Kb

This emulator is ROBOTC Virtual Worlds. Look here: http://www.robotc.net/download/rvw/
and here: [http://www.cs2n.org/rvw for more information.](http://www.cs2n.org/rvw for more information.)

The PC emulator works quite well, as you have observed the LCD display can be used as well as debugging windows. Timers are emulated and a few other things like the motors. There is no sensor input so that is always a problem but it does allow you to debug a certain amount of program flow. I have never tried to hook up a joystick, I think it only supports one from Logitech that I saw available for about $30.

magicode was referring to RobotC virtual worlds which is also fun to play with. This lets you control a couple of simple robots along with sensor feedback and a certain amount of physics simulation. They have a gateway competition field available as an environment to work in as well as other more exotic locations. CMU is offering a $30 discount on this through the end of October, see here http://www.robotc.net/blog/?p=1421

1 Like

So it was always inevitable that this thread would discuss multi-tasking at some point. RobotC’s ability to run more than one task is IMHO it’s greatest strength, there has been lot’s written describing multi-tasking in it’s different forms but when dealing with RobotC I like to think of it as the ability to run more than one independent program at a time even though these programs are contained within one executable and downloaded together. For some background reading on multi-tasking, wikipedia is not a bad place to start. http://en.wikipedia.org/wiki/Computer_multitasking.

Some will argue that most programs written for the VEX robotics system do not need to use multi-tasking. This to a certain extent is true, back when micro processors were not so powerful much embedded software used a technique where the “tasks”, in this case simply meaning different subroutines, were called one after the other in a so called “main loop”. Each subroutine would determine if it had any actions to take, perform them if necessary and then return. System functions needing immediate attention would be setup to use interrupts but great care had to be taken to make these special interrupt routines fast and efficient. Having said this some of the most complex embedded systems of the 60’s did use a primitive form of multi-tasking, one of the most famous of all was the Apollo guidance computer (AGC), which enabled NASA to land on the moon.

RobotC is, in fact, always using multi-tasking even when you have not created additional tasks yourself. It’s ability to display global variables, sensor values and other parameters is achieved using tasks that are started behind the scenes. These tasks only consume a small amount of the microcontrollers resources, perhaps 5%. We are not going to be concerned with how RobotC switches between tasks for now, perhaps a future post.

OK, so much for the history lesson.

The difficulty in creating a simple multi-tasking example is that almost everything proposed can be done without using multi-tasking, however, just to illustrate the principles involved we will create a very simple piece of code to flash two leds on and off connected to different output ports.

RobotC always needs a main task, this is the entry point for the code, it’s the equivalent of the main function in any C program. From the main task we will start a second task using the StartTask command with a single parameter, the name of the task to start. The call to start the second task is

StartTask( flashLed_1 );

Both the main and flashLed_1 tasks contain an infinite while loop, that is, a loop that never exits until the program is stopped. This loop prevents the tasks from running out of code to execute and therefore stopping. Sometimes allowing a task to finish can be useful but in general I would not recommend it, perhaps we will cover starting and stopping tasks dynamically in another post.

The contents of the while loop of each task is very similar, change the value on the output port and then wait for a preset amount of time, change the output port value back and then wait some more.

The code also includes another example of using the C preprocessor discussed in an earlier post.


#if (_TARGET == "Emulator")

This line is testing to see if the code is running on real hardware or on the PC emulator. The PC emulator can be very usefull but does not simulate real sensors, to overcome this in the example we substitute the real sensors for an array of integers by using a definition. If the code is compiled for a real cortex then


#define SENSOR      SensorValue

will be used, otherwise if running on the PC Emulator a different definition is used and an array also declared.

#define SENSOR      SensorValueSimulations
int SensorValueSimulations[kNumbOfRealSensors];

Here is the code to play with, if you try this in the PC emulator look for SensorValueSimulations[8] and SensorValueSimulations[9] changing as these represent the first two digital ports.

In part 2 on multi-tasking we will create a more complex example doing something more relevant to VEX robotics.

#pragma config(Sensor, dgtl1,  LED_1,               sensorDigitalOut)
#pragma config(Sensor, dgtl2,  LED_2,               sensorDigitalOut)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

// Detect if we are running in PC emulation and switch to
// using a simulation of the sensors as we cannot see the real leds
#if (_TARGET == "Emulator")
#define SENSOR      SensorValueSimulations
int SensorValueSimulations[kNumbOfRealSensors];
#else
#define SENSOR      SensorValue
#endif

// Flash the first LED  
task flashLed_1()
{
    while( true )
        {
        // output to 1
        SENSOR LED_1 ] = 1;
        wait1Msec( 500 );
        
        // output to 0
        SENSOR LED_1 ] = 0;
        wait1Msec( 500 );
        }
}

// Main task - flash the second LED
task main()
{
    // Start the other task
    StartTask( flashLed_1 );
    
    // Do our own thing
    while( true )
        {
        // output to 1
        SENSOR LED_2 ] = 1;
        wait1Msec( 250 );
        
        // output to 0
        SENSOR LED_2 ] = 0;
        wait1Msec( 250 );
        }
}
1 Like

That was a great informative post. I would like to add one thing however. If the tasks that you are running do not have ant wait time in them, i.e Wait1Msec(x), then the tasks might not execute properly. When you are not absolutely sure that a task will include wait time, then you must include this command somewhere in the while loop of your task so that it es executed on every iteration: “endTimeSlice();” I will demonstrate with an example:


task flashLed1(){
  while(true){
    SensorValue[LED1] = 0;
    wait1Msec(500);
    SensorValue[LED1] = 0;
    wait1Msec(500);
  }
}
//There is no endTimeSlice() needed here because the task always waits
//no matter what

task flashLed2(){
  while(true){
     if(SensorValue[button1]){
       SensorValue[LED1] = 0;
       wait1Msec(500);
       SensorValue[LED1] = 0;
       wait1Msec(500);
    }
    endTimeSlice();
  }
}
//This task needs a endTimeSlice() because if the button is not pressed,
//then there will be no wait time in the task

task main(){
  StartTask(flashLed1);
  StartTask(flashLed2);
  while(true){
    motor[port3] = vexRT[Ch3];
    endTimeslice();
  }
}
//The command is also needed in the main task here because the
//main task does not wait in the while loop.
1 Like

OK, part 2 then I need to do some real work today. This may be a little complex for some here but please don’t feel intimidated, it’s good to see a more fully developed example once in a while.

The previous post contained a very simple example demonstrating RobotC multi-tasking that would flash two LEDs at different speeds, not very exciting but actually useful in some cases. The task, flashLed_1, will keep executing no matter what other code you are running. Perhaps the robot is under driver control, LED_1 keeps flashing, perhaps following a line in autonomous mode, LED_1 keeps flashing. Why it’s flashing I have no idea, perhaps bad battery, that’s up to the programmer.

(one caveat here, when using the competition template, RobotC will stop all tasks started in Autonomous mode before entering driver control mode. You need to restart tasks you may want in both phases).

Now for a more complex example, hopefully not too complex but programmers of all abilities are presumably reading the forums.

One thing that I see that always worries me when students are doing motor control is that motors are being switched from full speed forwards to full speed backwards instantaneously. I’ve never seen any damage resulting from this, and I’m sure VEX has designed the motors to handle this stress, but it worries me all the same. To circumvent this situation we are going to use multi-tasking to implement a controlled acceleration and deceleration of the motors, I call this slew rate control, but it goes by other names as well.

To achieve this we will use an intermediate variable to hold the requested speed of the motor and a task to gradually change the current speed of the motor until it reaches the requested speed. As there can be up to 10 motors connected to the cortex microcontroller we need to track this for all 10 motors. We may not want the same acceleration and deceleration for all motors so we will also have a variable for each motor that determines how fast each one can change.

OK, a section by section breakdown of the code.

#pragma config(Motor,  port2,           DriveLeft_1,   tmotorNormal, openLoop)
#pragma config(Motor,  port3,           DriveLeft_2,   tmotorNormal, openLoop)
#pragma config(Motor,  port4,           DriveRight_1,  tmotorNormal, openLoop, reversed)
#pragma config(Motor,  port5,           DriveRight_2,  tmotorNormal, openLoop, reversed)

Usual RobotC setup, we have 4 motors, 2 on each side for driving the robot.

/*                                                                             */
/*  definitions used by driver control                                         */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

#define     JOY_DRIVE_V     vexJSLeftV
#define     JOY_DRIVE_H     vexJSLeftH
#define     JOY_THRESHOLD   15

definitions used by the arcade driver control

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  definitiona and variables for the motor slew rate controller.              */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

#define MOTOR_NUM               kNumbOfTotalMotors
#define MOTOR_MAX_VALUE         127
#define MOTOR_MIN_VALUE         (-127)
#define MOTOR_DEFAULT_SLEW_RATE 10      // Default will cause 375mS from full fwd to rev
#define MOTOR_FAST_SLEW_RATE    256     // essentially off
#define MOTOR_TASK_DELAY        15      // task 1/frequency in mS (about 66Hz)
#define MOTOR_DEADBAND          10

// Array to hold requested speed for the motors
int motorReq MOTOR_NUM ];

// Array to hold "slew rate" for the motors, the maximum change every time the task
// runs checking current mootor speed.
int motorSlew MOTOR_NUM ];

A bunch of definitions used by the task controlling acceleration and deceleration. An array, motorReq, to hold the requested speed for each motor. An array, motorSlew, to hold the change in speed for each motor each time we check it.

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  Task  - compares the requested speed of each motor to the current speed    */
/*          and increments or decrements to reduce the difference as nexessary */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

task MotorSlewRateTask()
{
    int motorIndex;
    int motorTmp;

    // Initialize stuff
    for(motorIndex=0;motorIndex<MOTOR_NUM;motorIndex++)
        {
        motorReq[motorIndex] = 0;
        motorSlew[motorIndex] = MOTOR_DEFAULT_SLEW_RATE;
        }

    // run task until stopped
    while( true )
        {
        // run loop for every motor
        for( motorIndex=0; motorIndex<MOTOR_NUM; motorIndex++)
            {
            // So we don't keep accessing the internal storage
            motorTmp = motor motorIndex ];

            // Do we need to change the motor value ?
            if( motorTmp != motorReq[motorIndex] )
                {
                // increasing motor value
                if( motorReq[motorIndex] > motorTmp )
                    {
                    motorTmp += motorSlew[motorIndex];
                    // limit
                    if( motorTmp > motorReq[motorIndex] )
                        motorTmp = motorReq[motorIndex];
                    }

                // decreasing motor value
                if( motorReq[motorIndex] < motorTmp )
                    {
                    motorTmp -= motorSlew[motorIndex];
                    // limit
                    if( motorTmp < motorReq[motorIndex] )
                        motorTmp = motorReq[motorIndex];
                    }

                // finally set motor
                motor[motorIndex] = motorTmp;
                }
            }

        // Wait approx the speed of motor update over the spi bus
        wait1Msec( MOTOR_TASK_DELAY );
        }
}

This is the task that sends new speeds to each motor. The code before the while statement initializes the variables to a known state. Within the while loop there is a for loop that iterates through each motor. The current motor speed is compared to the requested speed and incremented or decremented as necessary. The motor speed is limited to the exact speed requested and then sent to the motor. The task having checked each motor then goes to sleep for a defined time (15mS in this case) before being woken up again by the RobotC task scheduler.

void
DriveLeftMotor( int value )
{
    motorReq DriveLeft_1 ] = value;
    motorReq DriveLeft_2 ] = value;
}

void
DriveRightMotor( int value )
{
    motorReq DriveRight_1 ] = value;
    motorReq DriveRight_2 ] = value;
}

Two intermediate subroutines to control left and right drive of the robot. Instead of send a new value to the motor directly the new speed is written into the variable holding the new requested speed.

task ArcadeDrive()
{
    int    ctl_v;
    int    ctl_h;
    int    drive_l;
    int    drive_r;

    // Basic arcade control
    while( true )
        {
        // Get joystick H and V control
        ctl_v = vexRT JOY_DRIVE_V ];
        ctl_h = vexRT JOY_DRIVE_H ];

        // Ignore joystick near center
        if( (abs(ctl_v) <= JOY_THRESHOLD) && (abs(ctl_h) <= JOY_THRESHOLD) )
	        {
	        drive_l = 0;
                drive_r = 0;
	        }
	    else
	        {
                // Create drive for left and right motors
	        drive_l = (ctl_v + ctl_h) / 2;
	    	drive_r = (ctl_v - ctl_h) / 2;
	    	}

	    // Now send out to motors
	    DriveLeftMotor( drive_l );
            DriveRightMotor( drive_r );

            // don't hog CPU
	    wait1Msec( 25 );
	    }
}

This task controls the robot’s drive, it’s fairly standard arcade drive control, nothing fancy.

task main()
{
    // Start motor slew rate control
    StartTask( MotorSlewRateTask );

    // Start driver control tasks
    StartTask( ArcadeDrive );

    // Everything done in other tasks
    while( true )
        {
        wait1Msec( 100 );
        }
}

This is the main task and entry point for the program. The “MotorSlewRateTask” is started, the “ArcadeDrive” is started and then it enters a loop that does nothing. All control is handled by the other two tasks. Implementing main in this fashion allows the code to be moved into the competition template easily, to enter driver control simply start the two other tasks.

Now hitting the 10k limit so will post the source as an attachment. Any questions? fire away.
MultiTask.c (8.66 KB)

1 Like

Agreed, perhaps tomorrow we can cover some of the details of how the RobotC task scheduler actually works and some of the other commands available for task control.

1 Like

That would be great, This is really great information.

I had another question but just figured out the answer. I tried converting my LCD display code ( which cycles through a series of informational displays) from a subroutine to a task. It didn’t work initially, but I realize I need to add a while loop for a task. Thanks again for the great tips.

Thanks in advance. Kb

1 Like

I’m going to discuss multi-tasking a bit more and the task scheduler. As there is little documentation on this aspect of RobotC most of this discussion is a best guess and may be incorrect in some of the fine details.

Consider a group of students sitting around a table having a discussion with their teacher. If a student wants to speak then they have to raise their hand and wait for the teacher to give them permission. If several students have their hands raised the teacher will choose each one in turn so everyone can be heard. Some students may want to speak often and keep raising their hands, others may be sleepy and not participate much at all. The teacher is also watching a clock, if a student speaks for too long then the teacher will interrupt them and give someone else a turn. At one point the school principle joins the debate, he also has to raise his hand to be able to speak but, as he is considered more important, the teacher always gives him priority.

This analogy describes quite well how the RobotC task scheduler works. The task scheduler (the teacher in my example) determines which task (student) can run. The principle represents a high priority task that has precedence over the others. When several tasks of the same priority want to run the scheduler chooses them in turn. If a task continues running too long then the scheduler stops it and gives time to others.

To show how this works in practice I’m going to use a modified version of the code presented a couple of posts back that flashes two LEDs. I’ve modified the code a little so that everything happens much faster and we can observe the signal going to the LEDs on an oscilloscope. An oscilloscope lets us look at the level of a signal (it’s voltage) over time and creates a graphical representation. More background on oscilloscopes is here http://en.wikipedia.org/wiki/Oscilloscope.

Here’s the test code we will use, very similar to before but the while loops only wait for 1mS and 2mS respectively.

#pragma config(Sensor, dgtl1,  LED_1,               sensorDigitalOut)
#pragma config(Sensor, dgtl4,  LED_2,               sensorDigitalOut)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

// Flash the first LED
task flashLed_1()
{
    while( true )
        {
        // toggle output
        SensorValue LED_1 ] = 1 - SensorValue LED_1 ];

        wait1Msec( 1 );
        }
}

// Main task - flash the second LED
task main()
{
    // Start the other task
    StartTask( flashLed_1, 6 );

    // Do our own thing
    while( true )
        {
        // toggle output
        SensorValue LED_2 ] = 1 - SensorValue LED_2 ];
        
        wait1Msec( 2 );
        }
}

If we observe the digital outputs on the scope we see the following, the value of LED_1 is shown by the yellow trace, LED_2 the blue. LED_1 is changing state every 1mS, LED_2 is changing state every 2mS. This corresponds exactly to the wait1Msec delays we have in the two tasks, each task does it’s thing and then sleeps until woken again by the task scheduler.

TEK00006.jpg

For the next example we replace the wait1Msec(1) statement in the flashLed_1 task with a different command, AbortTimeSlice. This command hands control back to the task scheduler which then decides if any other tasks are waiting to execute (have their hands up), if none are waiting then control is given back to flashLed_1, lets see this on the scope.

// Flash the first LED
task flashLed_1()
{
    while( true )
        {
        // toggle output
        SensorValue LED_1 ] = 1 - SensorValue LED_1 ];

        AbortTimeSlice();
        }
}

TEK00007.jpg

You can observe that LED_1 is now flashing very fast as the only other task we have created is the main task which sleeps for 2mS before wanting to run. Both tasks still operate but the majority of the time is spent running the flashLed_1 task. If you are wondering what the large gap in the yellow trace is, well in the previous post I explained that RobotC already has other tasks running for debugging purposes, this gap occurred when the RobotC task, presumably a more important task, needed to run.

To be continued…….

3 Likes

In the last post we introduced AbortTimeSlice, RobotC also has the command EndTimeSlice which, with a little digging around in the system headers, turns out to do exactly the same thing as AbortTimeSlice.

For the next experiment I’m going to delete the AbortTimeSlice command completely and see what the result is, here’s the modified code.

// Flash the first LED
task flashLed_1()
{
    while( true )
        {
        // toggle output
        SensorValue LED_1 ] = 1 - SensorValue LED_1 ];
        }
}

And the waveform showing what the two LEDs are doing.

TEK00008.jpg

So not much changed but it is different from before. flashLed_1 did not voluntarily give control back to the task scheduler, however, the task scheduler was watching the clock and after a while said enough is enough and let the main task run. LED_2 controlled by the main task is still changing but not as consistently as before.

One more example for today, lets now make flashLed_1 a more important task, the correct terminology is a higher priority task. We do this by providing a second parameter to the StartTask command in the following way.


StartTask( flashLed_1, 10 );

Normal priority tasks are started with a priority of 7, if we provide a number which is higher then that task will have a higher priority (bigger number = more important).

Here’s the modified code.

// Main task - flash the second LED
task main()
{
    // Start the other task
    StartTask( flashLed_1, 10 );

    // Do our own thing
    while( true )
        {
        // toggle output
        SensorValue LED_2 ] = 1 - SensorValue LED_2 ];
        
        wait1Msec( 2 );
        }
}

And here’s the waveform.

TEK00009.jpg

You will notice that the main task (blue waveform) never runs, this is because the higher priority task does not give up control and therefore the task scheduler does not let the less important task run.

That’s it for now, I will post a little more to complete this over the weekend.

2 Likes

I’m going to conclude this short tutorial on multi tasking with a discussion of the remaining useful RobotC commands relating to tasks.

We have already used StartTask to start tasks with the default task priority or a user defined priority.

StartTask( taskName );

StartTask( taskName, priority );

We looked at how abortTimeslice ( and EndTimeSlice ) affect the way tasks run. Although I did not specifically discuss wait1Msec, it’s importance in allowing the task scheduler to switch between tasks was demonstrated.

The following commands will be used less frequently.


StopAllTasks();

This does what it’s name implies, stop all tasks from running including the main task. Not sure what the use of this really is other to terminate the program.


StopTask( taskName );

A bit more useful, stop the named task.

hogCPU();
releaseCPU();

The hogCPU command tells the task scheduler that the current task requires exclusive use of the cpu until either a releaseCPU call is made or the task sleeps. I don’t see the need for this in “normal” competition software, it can be used to stop the task scheduler switching tasks in the middle of time critical processing but, as it does not stop the RobotC background tasks from running, even that use is marginal.

getTaskPriority( taskName );
setTaskPriority( taskName, newPriority );

Use these to get and set the task priority for a currently running task. As above, I’m not sure these have much use during “normal” competition programming.

Before concluding I wanted to discuss briefly how long tasks should sleep in their processing loop. The cortex is running a form of Real Time Operating System, (RTOS), however, real time operation of every task is an illusion as the cortex can in fact only do one thing at a time. The concept of real time is very flexible, when a button is pressed on the joystick to make an action happen, for example a motor running or stopping, how fast this has to happen and whether it is considered real time is purely from the perspective of the operator. For this type of action 0.1 seconds has been used as a rule of thumb in many situations and would imply that checking joystick buttons needs to happen no faster than perhaps twice this speed, say every 50mS. The point I’m trying to make is that most tasks do not need to run that often and can spend most of their time sleeping. Updating motors or checking sensors any more than 50 to 100 times a second is not necessary so an average task can have wait1Msec(20) at the end of the processing loop without any issues.

The next post will probably be about semaphores sometime later this week.

2 Likes

Semaphores

A short example using semaphores.

Semaphores are used when two or more tasks want to access a resource that should only be used by one at a time. The resource could be a data structure or physical device such as the LCD display. There is lots of information about semaphores on the internet, as usual a good place to start is here http://en.wikipedia.org/wiki/Semaphore_(programming)

There are only a few calls needed to use semaphores, first a semaphore has to be defined in a similar way to any other variable.


TSemaphore  MySemaphore; 

Somewhere before the semaphore is used, normally in the main task, the semaphore needs to be initialized.

// Init the semaphore
SemaphoreInitialize(MySemaphore); 

When a task wants to gain exclusive access to a resource, the semaphore needs to be locked. A time can be specified for this call to give up if the semaphore cannot be locked, this defaults to around 32 seconds (hex 7FFF milliseconds), if there is any doubt that the semaphore was obtained then it’s status can be checked or the resource could be used with the possibility of incorrect program operation.

Lock with default timeout


SemaphoreLock( MySemaphore ); 

Lock with a 10mS timeout


SemaphoreLock( MySemaphore , 10 ); 

Check if the task has obtained the semaphore.

if ( bDoesTaskOwnSemaphore(MySemaphore) )
    {
    // do my thing
    }

Finally, the semaphore needs to be released when the task has finished using the resource. The task should not release a semaphore it has not successfully locked it so ownership should be checked.

// release the semaphore if we have it
if ( bDoesTaskOwnSemaphore(MySemaphore) )
    SemaphoreUnlock(MySemaphore); 

Here is a trivial example that has two tasks trying to write to the LCD, it is written to enhance the problem of not using a semaphore to make the issue easier to understand. Both tasks use the same function, DisplayChar, which first clears the character at the requested position and then writes a new character in the next column. A delay has been placed after the character clearing to cause the conflict to happen more often, if the second task runs at this point the first task may then write to the wrong LCD position. Run the program with ENABLE_SEMAPHORES disabled (commented out or deleted) to see the effect of not using a semaphore. The program runs just fine under the RobotC PC emulator so you can still try it if you don’t own an LCD.

Enjoy.

#pragma config(UART_Usage, UART1, VEX_2x16_LCD, baudRate19200, IOPins, None, None)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

// comment out this to show the issue of not using a semaphore
#define ENABLE_SEMAPHORES   1

// the semaphore variable
TSemaphore  MySemaphore;

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  A function to display a character at position row, col+1 after clearing    */
/*  the character at position row, col.                                        */
/*                                                                             */
/*  There is no purpose to this function other than part of the semaphore demo */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

void
DisplayChar( int &col, int row, char c )
{
    // col is passed by reference, ie. a pointer


    // Set to display at the current position
    setLCDPosition(row, col);
    // Wait a while to enhance problems with two tasks
    // calling this function
    wait1Msec( 100 );

    // Clear current character
    displayNextLCDChar( ' ' );
        
    // Increment the column, wrap if we reached the end
    if( ++col == 16 )
            col = 0;
                
    // Display the character at the new position
    setLCDPosition(row, col);
    displayNextLCDChar( c );    
}

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  A task to display a moving # on the upper row of the Vex Lcd               */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

task OtherTask()
{
    int     i = 0;
    
    while( true )
        {
#ifdef ENABLE_SEMAPHORES
        // Lock the semaphore - we will wait so no need for a timeout
        SemaphoreLock( MySemaphore );
#endif        
        // Display the moving #
        DisplayChar( i, 0, '#' );
        
        // release the semaphore if we have it
        if ( bDoesTaskOwnSemaphore(MySemaphore) )
            SemaphoreUnlock(MySemaphore);

        // Sleep
        wait1Msec( 500 );
        }
}

/*-----------------------------------------------------------------------------*/
/*                                                                             */
/*  The main task,  display a moving x on the lower row of the Vex Lcd         */
/*                                                                             */
/*-----------------------------------------------------------------------------*/

task main()
{
    int     i = 0;
    
    // LCD backlight on
    bLCDBacklight = true;
    
    // Init the semaphore
    SemaphoreInitialize(MySemaphore);

    // Start the other task
    StartTask( OtherTask );

    // clear both lines on the LCD
    clearLCDLine(0);
    clearLCDLine(1);

    // Do our own thing
    while( true )
        {
#ifdef ENABLE_SEMAPHORES
        // Lock the semaphore - we will wait so no need for a timeout
        SemaphoreLock( MySemaphore );
#endif
        // Display the moving x
        DisplayChar(i, 1, 'x' );
        
        // release the semaphore if we have it
        if ( bDoesTaskOwnSemaphore(MySemaphore) )
            SemaphoreUnlock(MySemaphore);

        // Sleep
        wait1Msec( 50 );
        }
}


semaphoreExample.c (3.58 KB)

1 Like

I want to share a solution I had to define for displaying floating point numbers on the LCD display. I tried a variety of options with the function

displayLCDNumber(nLine, nPos, nValue, nPrecision);

but I could not get a floating point to display properly so I did some digging and found this solution.

First define a string

 string printString;

Then using the function sprintf format output to the string and display the string. here’s the code to display the 3 coefficients for my PID control loop for display on the LCD:

      // Display PID Coeff Index 0 Left Wheel
      displayLCDCenteredString(0, "00 Prp Intg Drv");
      sprintf(printString,"%1.2f %1.2f %1.2f",propK[0],integralK[0],derivK[0]); 
      displayLCDCenteredString(1, &printString);

The Output looks like this:

00 Prp Intg Drv 
 0.20 0.25 0.50 

The coefficients are stored in arrays which allows me to write the PID function once and call it with the error value and an index value. This allows me to have effectively a different PID response per motor without having to re-create the PID code over and over. For PID tuning purposes I want to display the coefficients on the LCD display. sprintf allowed the use of print formatting to a string which I could then display on the LCD. Since I use an array to store different coefficients for each motor port (assuming they might all need specific coefficients) I pre-pend the index on the title display line. I’m sure this could be abstracted further to reduce code counts further but for now It’s just a static string for the PID index of interest.

Enjoy. Kb

edited 10/20 based upon feedback in this thread.

1 Like

One thing I need to point out here is this line of code creates an array of strings, the string type (not standard C) is assumed to be 16 bytes of data. So string printString[16]; uses 256 bytes of memory.

All you need is

 string printString;

The use of the & operator (address of) before the string also seems to be optional.

1 Like

This thread is awesome. Thank you to everyone putting your time, energy and experience into it.:smiley:

That said, I’d like to bring up this particular comment:

We hear you loud and clear. We’ve honestly been working to fill in the old gaps and continually document new features, but we’re not the end users, so we don’t see always what you see. If you know of specific areas that are weak or have other suggestions, please do share them. We really do try to be be responsive when we get feedback.

If you want to keep this thread from becoming a feedback thread, I’ll be monitoring this one in the ROBOTC Section: https://vexforum.com/showthread.php?t=55945

Thanks again.

1 Like

Hi Jesse

We “normal” users cannot post to your thread in the RobotC Technical Q&A forum, only the OP and you are able to do that.

The documentation is improving, I just checked 3.04 and many of the remnants from 2.32 are now gone. I would like, however, to see some description of the language itself, for example, referencing my previous post, how would I learn of the string type? How would I know what it’s memory requirement are? There are explanations of the commands using the string type but nowhere (that I have found) explains why I should use it over say an array of char. I would also like to see a standalone reference guide for all available functions and variables along with some technical discussion of the underlying architecture of RobotC. I have been trying to understand and document some of this in this thread but it would be better coming from CMU.

1 Like

Yeah, I would say that this is the holy grail that most of us have been looking for with ROBOTC. Whenever a new programmer would come to our robotics club, and start learning ROBOTC, they would eventually ask for an API only to be laughed at by the more experienced programmers.

Thanks for pointing out the array of string vs char. I initially tried to define the array as char printString[16]; but the compiler didn’t like that. I use the C programming language by Kernighan & Ritchie (RIP) which was based on the draft Ansi C when I got it. It seems to define the argument for sprintf as a char * so thats what I tried first. There is no entry in the help file as has been pointed out for the various functions. what is an sString ? when I google it, it seems to be a c++ class, so there is an odd mix of C++ with this C like compiler.

gnu joke here maybe RobotC should be called C less vs C like ? less is more?

Anyway I like the functionality I would like to know how to use it without guessing and trying to figure out what might work.

Cheers Kb