News
A first corner - ahhh
by Martyn on May.15, 2009, under News
Well it made its first corner, video:
Go to http://www.youtube.com/watch?v=5a4zVuxzEdg for the HD version.
And here is the data from that run:
The todo list is as follows:
- Retune controller (there is some nasty overshoot and the control signal itself is oscillating)
- Calibrate the speeds - left and right wheel have different speeds (:() so need to go an work out the difference
- Automate this, cause it took all day to get to this point
Please note that this is a ‘jigged’ callibration - the long straight allows me to guestimate the required speed differences and ticks per meter, so not too precise at this point in time.
First Mouse Movements
by Martyn on Apr.04, 2009, under News
Very bad video quality - but the mouse moves:
Will get a better camera in on Monday, but yay it moves. This is a very basic movement - accelerate to ~20% maximum speed, stay there and start to decelerate after 1s.
It moved very roughly, 0.5m in 1sec, making its theoretical (ish) top speed about 2.5m/s (when loaded). Although that metric does not take into account anything, so could be higher or lower :p.
Buttons - and LEDs :D
by Martyn on Apr.04, 2009, under Microcontrollers, Micromouse, News, Robots
Buttons are cool, so are menu’s. So with the help of a button, a few leds and a dspic I have a menu system (also note that this is blocking, the idea is that its used to start / stop the robot, so the robot does not move while the menu is active), first lets look at the switch circuit and then at the dspic set up (c30 compiler, using a dspic33fj128gp804):
/* Setup the IO Pin Assignment */ __builtin_write_OSCCONL(OSCCON & ~(1<<6)); RPINR7bits.IC1R = 5; // Switch Input Capture __builtin_write_OSCCONL(OSCCON | (1<<6)); /* Switch Input Capture Setup */ IC1CONbits.ICM = 0x02; // Capture on falling edge _IC1IF = 0; _IC1IE = 1; /* CPU Time Clock */ T1CONbits.TCKPS = 0x02; // 40.5504Mhz / 64 TMR1 = 0x00; PR1 = 634*4; // Int at 1.000631313ms*4 _T1IF = 0; _T1IE = 1; T1CONbits.TON = 1;
The code is fairly simple: first attach the switch input to the input capture (ic1) peripheral, set up ic1 to trigger an interrupt on the falling edge and then there is the set up for my main timer loop, this occurs at ~250Hz, and yes I am using a weird value crystal.
Next lets look at the global variables (well global to the scope of the interrupts)
// Switch input vars int sw_counter = 0; int sw_on = 0; // Menu vars int menu_counter = 0; int menu_on = 0; int menu_value = 0;
Next the interrupt for the input capture routine
void __attribute__((__interrupt__)) _NOPSV _IC1Interrupt(void)
{
_IC1IF = 0; // Clear the interrupt
_IC1IE = 0; // Disable the interrupt
_RED = 1; // Show the user the button pressed
sw_on = 1; // Enable the debounce counter
// If the menu is on already, increment the menu value
// Otherwise turn the menu on and set the menu value to
//default
if (menu_on)
{
menu_val ++;
menu_counter = 0;
}
else
{
menu_on = 1;
menu_val = 0;
}
}
And then the interrupt for the timer:
void __attribute__((__interrupt__)) _NOPSV _T1Interrupt(void)
{
// Handle switch
if (sw_on) // If a switch 'event' is ongoing
{
sw_counter ++;
if (sw_counter > 50) // 50x 1/250 = 200ms debounce
{
sw_on = 0; // Switch no longer on
sw_counter = 0; // Counter to zero
_IC1IF = 0; // Clear the bounces
_IC1IE = 1; // Enable more switches
_RED = 0; // Led off
}
}
// Menu
if (menu_on) // If menu is on
{
menu_counter ++; // Increment counter
// Leave menu active for 1s after last click
if (menu_counter > 250)
{
menu_on = 0; // Menu off
menu_counter = 0; // Reset
// Menu complete - do stuff
switch (menu_val)
{
case 0:
_RED = 0;
Nop(); // Required if RED and GREEN
// On same port
_GREEN = 1;
break;
case 1:
_RED = 1;
Nop(); // Required if RED and GREEN
// On same port
_GREEN = 0;
break;
default:
break;
}
}
}
}
And thats it, google _NOPSV to find the macro for that (sometimes required to compile using c30). Also _RED and _GREEN are port bits, so something like #define _RED _RB1 to make _RED = 1 turn Port B pin 1 on. All quite useful, maybe…
Its must have been a good day for someone…
by Martyn on Apr.02, 2009, under Microcontrollers, Micromouse, News, Photoblog, Robots
Today was a very unproductive one. Slept in, managed to do nothing till 2, then spend hours trying to work out why something had broken on my mouse - it was me, it was running 4 times slower because I put the line of code to tell it to run 4 times faster in the wrong place, that followed by a c# bottleneck - c#.net cannot handle 1mbit rs232 too well, its running fine at about 20% capacity, but between there and 50% capacity something goes very wrong. That lead to hours of no progress. And finally I though I may have broken one of my motors, the expensive ones, but it was fine, a bad connector, or maybe a break in the cable (think it was the former as after a lot of wiggling I cant detect a break). It must work - look at the graph bellow
result.

Graph if step input to motor
The ‘rough’ steady state speed of this input is about 2m/s, mwhahaha, lets hope there is some torque at 0.3m/s otherwise testing will be interesting….
CRC With dsPIC
by Martyn on Mar.23, 2009, under News
CRC’s are probably the most time consuming thing you can do in a data stream, but they have their uses - and not just for checking that data is correct, but more on that in a later post.
I found that the microchip documentation extremely useful, and I suspect you will to: Section 36 PCRC. But the biggest shout out should go to billysdomain in the Microchip Forums who actually got it working, the bits which make it work are his code, lets begin:
-
-
int CalculateCRC(DataPL *DataPacket)
-
{
-
unsigned int j, length = (DATAPL_LEN*2)-2;
-
unsigned char *ptr, *data;
-
ptr = (unsigned char *)&CRCDAT;
-
-
CRCCON = 0×0F; // Configure the polynomial length (PLEN)
-
_CRCIF = 0; // Clear the CRC flag
-
_CRCIE = 1; // Enable the int
-
-
CRCXOR = 0×1021; // Polynomial - crc-16
-
CRCWDAT = 0×0000; // Initialize CRCWDAT with 0
-
-
data = (*DataPacket).CHARS;
-
-
length >>= 1;
-
for(j = 0; j < length; j++)
-
{
-
//Must use pairs of bytes to get the correct result
-
*ptr = *data++; //write data into FIFO
-
*ptr = *data++; //write data into FIFO
-
-
if (CRCCONbits.CRCFUL) //check if FIFO is full
-
{
-
CRCCONbits.CRCGO = 1; //start CRC engine
-
while (!CRCCONbits.CRCMPT); //check if FIFO is empty
-
CRCCONbits.CRCGO = 0; //stop CRC engine
-
}
-
}
-
-
CRCDAT = 0×0000;
-
CRCCONbits.CRCGO = 1; // (Re)Start the calculation
-
-
comms_datapacket = DataPacket; // Save a reference
-
// With a bit of luck CRC finishes before we send
-
// the data via SPI…
-
-
return 1;
-
}
-
-
//Use the software CRC calculator
-
static const unsigned int CRC16Table[256] = {
-
0×0000,0×1021,0×2042,0×3063,0×4084,0×50a5,0×60c6,0×70e7,
-
0×8108,0×9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
-
0×1231,0×0210,0×3273,0×2252,0×52b5,0×4294,0×72f7,0×62d6,
-
0×9339,0×8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
-
0×2462,0×3443,0×0420,0×1401,0×64e6,0×74c7,0×44a4,0×5485,
-
0xa56a,0xb54b,0×8528,0×9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
-
0×3653,0×2672,0×1611,0×0630,0×76d7,0×66f6,0×5695,0×46b4,
-
0xb75b,0xa77a,0×9719,0×8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
-
0×48c4,0×58e5,0×6886,0×78a7,0×0840,0×1861,0×2802,0×3823,
-
0xc9cc,0xd9ed,0xe98e,0xf9af,0×8948,0×9969,0xa90a,0xb92b,
-
0×5af5,0×4ad4,0×7ab7,0×6a96,0×1a71,0×0a50,0×3a33,0×2a12,
-
0xdbfd,0xcbdc,0xfbbf,0xeb9e,0×9b79,0×8b58,0xbb3b,0xab1a,
-
0×6ca6,0×7c87,0×4ce4,0×5cc5,0×2c22,0×3c03,0×0c60,0×1c41,
-
0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0×8d68,0×9d49,
-
0×7e97,0×6eb6,0×5ed5,0×4ef4,0×3e13,0×2e32,0×1e51,0×0e70,
-
0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0×9f59,0×8f78,
-
0×9188,0×81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
-
0×1080,0×00a1,0×30c2,0×20e3,0×5004,0×4025,0×7046,0×6067,
-
0×83b9,0×9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
-
0×02b1,0×1290,0×22f3,0×32d2,0×4235,0×5214,0×6277,0×7256,
-
0xb5ea,0xa5cb,0×95a8,0×8589,0xf56e,0xe54f,0xd52c,0xc50d,
-
0×34e2,0×24c3,0×14a0,0×0481,0×7466,0×6447,0×5424,0×4405,
-
0xa7db,0xb7fa,0×8799,0×97b8,0xe75f,0xf77e,0xc71d,0xd73c,
-
0×26d3,0×36f2,0×0691,0×16b0,0×6657,0×7676,0×4615,0×5634,
-
0xd94c,0xc96d,0xf90e,0xe92f,0×99c8,0×89e9,0xb98a,0xa9ab,
-
0×5844,0×4865,0×7806,0×6827,0×18c0,0×08e1,0×3882,0×28a3,
-
0xcb7d,0xdb5c,0xeb3f,0xfb1e,0×8bf9,0×9bd8,0xabbb,0xbb9a,
-
0×4a75,0×5a54,0×6a37,0×7a16,0×0af1,0×1ad0,0×2ab3,0×3a92,
-
0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0×9de8,0×8dc9,
-
0×7c26,0×6c07,0×5c64,0×4c45,0×3ca2,0×2c83,0×1ce0,0×0cc1,
-
0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0×8fd9,0×9ff8,
-
0×6e17,0×7e36,0×4e55,0×5e74,0×2e93,0×3eb2,0×0ed1,0×1ef0
-
};
-
-
unsigned int ChecksumCalcS(unsigned char *data, unsigned int len)
-
{
-
unsigned int crc = 0;
-
unsigned int i;
-
for(i = 0; i < len; i++)
-
{
-
crc = (crc << 8) ^ CRC16Table[((crc >> 8) ^ *data++)];
-
}
-
return crc;
-
}
-
-
void __attribute__((__interrupt__)) _NOPSV _CRCInterrupt(void)
-
{
-
unsigned short hardware, software;
-
-
_CRCIF = 0; // Clear the interrupt
-
-
while(!CRCCONbits.CRCMPT); // Wait for CRC shifter to clear FIFO
-
(*comms_datapacket).CRC = CRCWDAT;
-
-
hardware = CRCWDAT;
-
software = ChecksumCalcS((*comms_datapacket).CHARS,
-
(DATAPL_LEN*2)-2);
-
-
CRCWDAT = 0;
-
}
-
Ok, a lot of things to note
- This code will not work on its own
- Putting a breakpoint on the last line of code will allow a software / hardware comparison
- _NOPSV is a macro (google it)
- The DataPL datatype is a union of an int array, char array and a large bitfield which allows me to have loads of small (or large) data types of non-standard size.
- The interrupt is not required if working with more than 8 bytes of data as you need to wait for the buffer to clear before filling it - so might as well wait for it all to happen
- This uses 8-bit data rather than 16-bit data, if you have a working example of this with a software table based alternative please email me! the table means I can calculate the CRC in C#
- Most of code is by billysdomain - I was 80% there, missing 2 lines in my implementation, should have rtfm (more)
Anyhow, hope this helps someone. I might do a quick post on using CRC for synchronising serial streams soon, guess how that’s done :p




