In my last article I covered how you can use arrays with our new version of Yakindu Statechart Tools Professional Edition. In this article the project will be expanded and some pointer magic will be included to add a control system that manages a robot's motors depending on the state of the sensor.
Usually DC motors are controlled with an H-bridge circuit. In this setup, the motor has two variables – the running state and the current speed. The motor can be in brake, idle, forward- or reverse-running mode and the speed is normally controlled via PWM with an 8-bit number ranging from 0 to 255.
To describe this, two types are defined – an enum and a struct:typedef enum motormode {
STOP,
IDLE,
FWD,
RWD
} motormode_t;
typedef struct motor {
motormode_t mode;
uint8_t speed;
} motor_t;
Consider the following statechart declaration:
You can see that the variables motL and motR are defined as pointers to motor_t variables and that dist_p is a pointer to an unsigned int of 8 bit width. In this way they can be passed in simply after we allocated the statechart with the setter-functions and accessed from the statechart.
Take a look at the statechart. On entry the statechart goes into the state p_test, short for pointer test. On the next cycle, the correct setting of the used pointers is checked. The user is intended to initialize them properly before entering the statechart from within his code. If the pointers are not set, this is considered a programming error, and thus the final state pointer_error is reached.
If that test is successful, the normal operation is entered: this means the state stop is activated. Instead of directly starting the motors, the robot waits for the go event – what absolutly makes sense: More than one robot has accidentally found the edge of the desk after a reset, which is probably not what you want it to do. This way, you can put the robot on a safe driving surface, before e.g. you push a button to activate the drive state.
In this state drive there are two possibilities – either there is no obstacle in front and the robot will drive straight or an obstacle is encountered and the robot will start to turn left until the measured distance value is high enough again. That’s a very simple design. More complex approaches could randomize the direction of turning or the duration, depending on the intended mode of operation.
Note how the three pointer-variables are accessed. The sensor measurement is read out with sensor.dist_p.value - value is a feature call on the pointer variable, returning its underlying real value (which could very well be another pointer). The same syntax allows to write through the motL and motR pointers to the real structs in the four states that alter the motor speed and mode. If there’s a variable and you need a suitable pointer you can use the feature call pointer similarly.
The state sensor_error is entered when the in event sensorfault is raised. This is meant to be done by another component, which manages the sensors and monitors their behavior. Remember the last article: the sensor raised an error whenever its measured values’ standard deviation was too high, indicating a weird measurement. The managing unit could react on that event and raise the sensorfault event when the sensor raises its error event three times in a row, which would stop the robot before it crashes in a wall because it has suddenly become blind.
Now you know the projekt let's give you a little background knowledge why you should use pointers: the aim is to have a normal C function that regularly writes the desired motor settings to the hardware. When the motor_t variables are defined in the main-function, it can pass them in that hardware function and pass pointers to them to the statechart. That way, the statechart manages what it wants to do with the motors. The underlying function handles how it’s done and doesn’t need to know where the values come from.
A much simpler approach would be to access the statechart’s variables in the hardware function via its handle from the motor function, but this would come with the cost of a much tighter coupling between the system’s components. With the design used here, the motL and motR variables in the statechart can be renamed without the need to adapt the outer system, except for the two setter functions. You could even define your own operation that sets these pointers because operations in a statechart can return pointers and use them just like any other type.
Also, the measured distance value from the last article is meant to be passed in as a pointer, so the statechart doesn’t need to call any function to get access to it and doesn’t need to know its source either. The outer system manages the sensor and its operation, possibly raising the sensorfault-event mentioned earlier.
Let’s summarize what you learned in this article about pointers:
Want to try YAKINDU Statechart Tools? Start now! You can find this example in our example wizard!