Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
Operating system
Development tools
Security and connectivity
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
Hi Chris,
When you start a section of code that can't be interrupted set a flag, then on the ticker look for the flag - if it is set exit the routine if not continue.
There is also some low level code that disables interrupts which might work - I'm not sure where it is but someone will reply here with it!
I wouldn't "mess" with trying to disable Timer3 interrupt (which is what the Mbed library uses). You could end up with a mess.
There's possible a few solitions.
Hi Chris,
Take a look at Working with Interrupts. It currently shows the basics of enabling and disabling interrupts, which should be exactly what you need.
Simon
enable_irq()/disable_irq() was exactly what I was looking for. Thanks. It would be nice if there was better documentation of those kids of features.
It works for a while, but my program hangs up. I suspect that I need an error handler for when the serial receive gets out of sorts.
disabling/enabling irqs can help out but before using them you have to think carefully on whats going to happen.
You have a Ticker caling back 20per/second, thats every 0.05seconds. Assuming your serial port is going at 9600baud, that's (approx) 960 characters per second.
But you only have 0.05seconds, so the most you can send is 48characters. And that time assumes a buffer with 48chars ready to go and no time for any logic.
If you Ticker callback function is longer than the time between ticks then you are storing up problems. Eventually something will give up and that's possibly why your system is hanging. The fact you are having to disable the irq on entry almost points to this being the case.
MODSERIAL (I mentioned eariler) will help a bit as it buffers your serial output so your serial printf functions will return faster. But eventually, you will fill it's output buffer and same thing will happen.
If you have a scope available try setting an output pin at the start and clearing it at the end of the ticker callback. That way you can measure how long you are spending inside your callback.
My serial port is going at 57600 baud. I'm only sending at most 5 bytes and receiving 26. The most I figure my interrupt can take is 4 or 5 ms, so that can't be the problem. The disable/enable irq is to keep the receive routine from interrupting a send routine. Yet it IS hanging up.
I am also using a couple of "wait" calls to debug (toggling pins at various points). Would wait interfer with ticker?
Btw...
I'm only sending at most 5 bytes and receiving 26
At 57000baud you shouldn't have a problem. However, without scoping out what's happening there's still an issue. Basically, how much time is it between sending your 5 bytes and getting back the first of your 26 expected bytes? How long it is till you have back the last byte of the 26? The entire exchange has to occur within your Ticker time. But without seeing your code it's rather hard to determine what's going on.
Here is the code. The basic idea is to have the Roomba sensors update the mbed in the background using the ticker interrupt.
#include "mbed.h" /* ROI - Roomba Open Interface */ #define _start 0x80 #define _baud_rate 0x81 #define _control 0x82 #define _safe 0x83 #define _full 0x84 #define _power 0x85 #define _spot 0x86 #define _clean 0x87 #define _max 0x88 #define _dock 0x8f #define _drive 0x89 #define _motors 0x8a #define _leds 0x8b #define _songs 0x8c #define _play 0x8d #define _sensors 0x8e #define _reset 0x07 /* physical sensors */ char bumpers; char wall; char cliff_left; char cliff_front_left; char cliff_front_right; char cliff_right; char v_wall; char motor_ovr_cur; char dirt_detect_left; char dirt_detect_right; /* buttons */ char remote; char buttons; int distance; int angle; /* power sensors */ char charging_state; unsigned int v_batt; int cur_batt; char t_batt; unsigned int batt_chg; unsigned int batt_cap; DigitalOut myled(LED1); /* code */ Serial roi(p9, p10); DigitalOut dd(p8); /* output commands */ void roi_init(void) { roi.baud(57600); dd=1; } void roi_startup(void) { __disable_irq(); dd=0; wait(0.5); dd=1; wait(2); roi.putc(_start); roi.putc(_control); roi.putc(_full); __enable_irq(); } void max(void) { __disable_irq(); roi.putc(_max); __enable_irq(); } void clean(void) { __disable_irq(); roi.putc(_clean); __enable_irq(); } void spot(void) { __disable_irq(); roi.putc(_spot); __enable_irq(); } void dock(void) { __disable_irq(); roi.putc(_dock); __enable_irq(); } void reset(void) { __disable_irq(); roi.putc(_reset); __enable_irq(); } void drive(int velocity, int radius) { __disable_irq(); roi.putc(_drive); roi.putc(velocity>>8); roi.putc(velocity); roi.putc(radius>>8); roi.putc(radius); __enable_irq(); } void motors(char motor_bits) { __disable_irq(); roi.putc(_motors); roi.putc(motor_bits); __enable_irq(); } void leds(char bits, char color, char intensity) { __disable_irq(); roi.putc(_leds); roi.putc(bits); roi.putc(color); roi.putc(intensity); __enable_irq(); } /* input commands */ DigitalOut led4(LED4); void get_sens(void) { __disable_irq(); led4=!led4; roi.putc(_sensors); roi.putc(0); bumpers=roi.getc(); wall=roi.getc(); cliff_left=roi.getc(); cliff_front_left=roi.getc(); cliff_front_right=roi.getc(); cliff_right=roi.getc(); v_wall=roi.getc(); motor_ovr_cur=roi.getc(); dirt_detect_left=roi.getc(); dirt_detect_right=roi.getc(); remote=roi.getc(); buttons=roi.getc(); distance=roi.getc()<<8+roi.getc(); angle=roi.getc()<<8+roi.getc(); charging_state=roi.getc(); v_batt=roi.getc()<<8+roi.getc(); cur_batt=roi.getc()<<8+roi.getc(); t_batt=roi.getc(); batt_chg=roi.getc()<<8+roi.getc(); batt_cap=roi.getc()<<8+roi.getc(); __enable_irq(); } char bump_left(void) { return bumpers & 2; } char bump_right(void) { return bumpers & 1; } char wheel_drop_right(void) { return bumpers & 4; } char wheel_drop_left(void) { return bumpers & 8; } char wheel_drop_cster(void) { return bumpers & 0x10; } char side_brush_ovc(void) { return motor_ovr_cur & 1; } char vacuum_ovc(void) { return motor_ovr_cur & 2; } char main_brush_ovc(void) { return motor_ovr_cur & 4; } char drive_right_ovc(void) { return motor_ovr_cur & 8; } char drive_left_ovc(void) { return motor_ovr_cur & 0x10; } char button_max(void) { return buttons & 1; } char button_clean(void) { return buttons & 2; } char button_spot(void) { return buttons & 4; } char button_power(void) { return buttons & 8; } Ticker tick; DigitalOut led1(LED1); DigitalOut led2(LED2); DigitalOut led3(LED3); int main() { led1=1; roi_init(); led1=0; roi_startup(); tick.attach_us(&get_sens,100000); while (1) { leds(2,128,128); led3=!led3; wait(0.1); leds(1,1,255); wait(0.1); led1=bump_left(); led2=bump_right(); } }
All those enable/disable of interrupts really just shouldn't be there. If they are needed then there's something fundamentally wrong. Specifically, having many comms IO calls within a function that is attached to a callback like that will always end up being an app killer. The comms should be moved out of the callbacks.
Additionally, I previously mentioned trying MODSERIAL and I think you'll found it may well have helped to some degree. However, MODSERIAL adds extra functionality you can use to implement what you are trying to do. Here's some sample code I knocked up:-
#include "mbed.h" #include "MODSERIAL.h" #define _sensors 0x8e /** * @file Sample program to demo getting the sensor data * @see http://www.roombadevtools.com/docs_roombasci.pdf * * Only a sample, doesn't actually get them all, see the code */ char bumpers; char wall; MODSERIAL roi(p9, p10); Ticker tick; bool getSensorsFlag = false; void get_sensors_callback(void) { // Don't send another request if the last 26 // have not yet been completely received. if (!getSensorsFlag) { getSensorsFlag = true; } } void request_sensors(void) { if (getSensorsFlag == true) { getSensorsFlag = false; if (roi.rxBufferGetCount() == 0) { roi.putc(_sensors); roi.putc(0); } } } int get_sensors(void) { if (roi.rxBufferGetCount() == 26) { bumpers = roi.getc(); wall = roi.getc(); // ... continue getting your other parameters. // ... then ensure the rx buffer is clean. roi.rxBufferFlush(); getSensorsFlag = false; return 1; } return 0; } int main() { roi.baud(57600); tick.attach(&get_sensors_callback, 0.1); roi.rxBufferFlush(); while(1) { // If the ticker callback has set the flag, send the request. if (getSensorsFlag == true) { request_sensors(); } // get_sensors() will return 1 when it's read teh 26 characters, // otherwise zero. if (get_sensors() != 0) { // horray, we should have all your data. } } }
Notice how the ticker callback just sets a flag. In the main loop if the flag is set and the rx buffer is clear (empty) we send the command. Then, in the main loop we check how many characters have arrived into the serial buffer. Once we have all 26bytes, it's at that point we read them into their variables. Notice also we don't send and request for more data until we know for sure we have all the data from the last request.
Also notice that get_sensors() doesn't start trying to read the data from the serial port until it has the 26 characters in it's buffer.
The above code does compile, however, it's not complete, just an example for you to look at.
The problem is that it doesn't do what I want it to do. I want to collect the sensors whilst the mbed is doing other things. Your code only starts an update the sensors at the beginning of the main loop. I want to be able to forget about asking it to update
Also, I can't send anything out other than request sensors. The roomba can't send and receive at the same time.
The point of making the request sensors routine the interrupt and leaving the command routines in the main code is that you can't do both at the same time. I am either sending a command to the roomba or receiving data from it. The enable/disable code is to prevent the send command from colliding with a request data command. The reason there are a lot of them is that I made several simple routines rather than one more complicated routine.
The problem isn't buffering the incoming data, it's keeping the send data routine from being messed up by the get data routine.
I am obviously missing something here. I'll chalk it up to my inexperience with C++ (I'm fine with C, but C++ drives me nuts) and the maddening lack of documentation for Mbed.
The enable/disable code is to prevent the send command from colliding with a request data command
Have to be honest here, your use of the enable/disable is just down to poor design of the program. What you need to do is create a state machine that manages the flow of data to/from the Roomba. MODSERIAL implements callbacks when it gets a character. SO, for example, you have a variable called "int roi_state;" and you use that to manage the flow control. So, for example:-
#include "mbed.h" #include "MODSERIAL.h" #define ROI_STATE_IDLE 0 #define ROI_STATE_GET_SENSORS 1 #define ROI_STATE_COMMAND 2 int roi_state = ROI_STATE_IDLE; #define _sensors 0x8e #define _drive 0x89 /** * @file Sample program to demo getting the sensor data * @see http://www.roombadevtools.com/docs_roombasci.pdf * * Only a sample, doesn't actually get them all, see the code */ char bumpers; char wall; MODSERIAL roi(p9, p10); Ticker tick; void tick_callback(void) { if (roi_state == ROI_STATE_IDLE) { roi_state = ROI_STATE_GET_SENSORS; roi.rxBufferFlush(); roi.txBufferFlush(); roi.putc(_sensors); roi.putc(0); } } void serial_callback(void) { if (roi_state == ROI_STATE_GET_SENSORS) { if (roi.rxBufferGetCount() == 26) { bumpers = roi.getc(); wall = roi.getc(); // ... continue getting your other parameters. // ... then ensure the rx buffer is clean. roi.rxBufferFlush(); // Reset the state machine back to idle. roi_state = ROI_STATE_IDLE; } } } // This just one example of how to send a command // to the Roomba void drive(int velocity, int radius) { // If the serial system is not idle we // must wait for it to become idle. // However, this causes your program to "block" // Wait. You could have this function return // immediately with an error code instead of // waiting around. while (roi_state != ROI_STATE_IDLE) ; // Now, claim control of the serial system // so we can safely send a command. // We loop to ensure we get control so // that if an interrupt "nips in" and // changes state we don't go on in error. while (roi_state != ROI_STATE_COMMAND) { roi_state = ROI_STATE_COMMAND; } // We now safely have control. roi.putc(_drive); roi.putc(velocity>>8); roi.putc(velocity); roi.putc(radius>>8); roi.putc(radius); // Assuming that's it, reset the state machine // back to idle and release serial control. roi_state = ROI_STATE_IDLE; } int main() { roi.baud(57600); // Ensure the buffers are clean before attaching // the interrupts to serial and ticker. roi.rxBufferFlush(); roi.txBufferFlush(); roi.attach(&serial_callback, MODSERIAL::RxIrq); tick.attach(&tick_callback, 0.1); while(1) { // Main loop of your program. Above is an // example of sending commands so as not // to conflict with putc/getc made via // interrupts. } }
The above program will get the sensor data "in the background". However, the state machine is to ensure there's no conflict between how the interrupt callbacks use the serial port and how your main program uses it. When two or more devices communicate it's important you develope a "protocol" rather than just send/receive bytes in an adhoc fashion. The Internet would desolve in seconds without the Internet Protocol.
I doubt it's a C++ verse C issue. The Mbed librraies maybe C++ but your program is effectively C. I think the real issue is you may not be use to interrupts in the embedded enviroment. If you are not use to handling them they can really give you a bad day.
btw, in teh simple program above I only created #define ROI_STATE_COMMAND 2
If command you send may make the Roomba send a reply you may have to define a state for each command. That way your serial_callback() function knows what to do with incoming characters. The state machine defines "whats happening" to all parts of your system.
Also, use MODSERIAL and use the roi.rxBufferGetCount() function to check for how many character are in the buffer verses what state you are in. Doing this means you never hang around inside an interrupt callback. So, if you have issued a command and you know you expect two bytes back from that command then in your serial callback you can check what command was issued verses how many characters are available to read. Only when the Roomba has sent all expected bytes back to you do you try to read them. That's what MODSERIAL is really developed for. It allows you to collect up a "packet" but only act on it once you have received the complete packet of bytes rather than trying to read them one by one or worst still do a getch() that stalls out your entire system waiting on just one byte.
You mentioned C vs C++ earlier. I can understand that, I can remember trying C++ after many years of C/Assembler and think "why? just why?". But one day the light dawned.
Now, as I said, your program is actually C. Here's a C++ "object" that may make your application alot easier:-
So, what's this all about? Well, first up, I don't have a Roomba so this class is totally untested. However, it should be a good starting point. Why bother doing this? Well, look at main.cpp. Once you have created a Roomba object using it is simple. There's only two functions you need. command() and sensor(). All that nitty gritty serial stuff and interrupts is hidden out of sight in the class/object. As you can see, once the Roomba class works fine you can concentrate on the fun bit which is your application and making it do cool stuff.
This is where C++ really comes into it's own. Controlling your entire robot with just two simple functions.
Damn! It was sort of the idea to write this up as a library. You beat me to it.
Oh well. Mine wasn't working anyway.
OK. How do we get different size packets? The roomba is able to send different sized packets for different sensor groups. For the simpler roomba model, 26 bytes is the max. The Create and the more advanced roombas can send up to 52 bytes. It might be good to change the number of bytes received. I'm thinking a flag that serial_callback reads to determine the number of bytes read and the how they re processed.
Hi Chris,
Well, it seems to me you still have to write this up as a library, I just gave you "a leg up" :)
What I basically did was build that from the datasheet I found that showed it pretty much static. I didn't realize there were many models!
Is there a way in the protcol somewhere that allows you to query what model it's connected to? If so, get that first and then store it as a class property. From that info you can use it to modify how the state machine may work and how the serial interface works.
And here's a fun C++ exercise. Assuming the Roomba so far is teh "base model" you can use the class as a "base class". You then create new classes for each model that inherit the base class (so getting the base functionalty) and just add to your new class the new bits the next model up offers.
But that depends on how much the Roombas build up. If they differ markedly in serial protocol for example, you won't gain much.
Anyway, I'll leave you to play with what I've done so far. It was a "get you going" exercise and get rid you of those pesky IRQ problems. I think now you'll see probably the best way to go about building your project. And if you win the Mbed Challenge with it you owe me a beer :)
This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.
I am writing a program that controls a device over the serial port. I am writing a routine that periodically updates the state of the device. Not a problem, I will use "ticker" to run the routine about 20 times a second.
The wrinkle is this, sending commands to the device requires a variety of data bytes that can not be interrupted or the device hangs. I can't have a command sequence being interrupted by the update sequence.
Is there a way to turn off the ticker process temporarally?