Blutack

In dedication to one of the most useful things in my room…Blutack

Oh blutack, why art though so awesome? You gotta love these things, they help people like me who’s lousy in fabrication. This post is dedicated to you, since you helped create it.

Pan and tilt system using two servos and the yaw and pitch axes

That’s right! This is just an upgrade to my last video. This time I’m using two servos and utilising two axes(axii?). As I’ve said above this video is dedicated to the almighty blutack, and you’ll understand what I mean when you see the photos below. I was meaning to post earlier but just got off work a couple of hours ago, I just wanted to do something rough before I hit the hay as the goddess of luck and robotics smiled upon me when I asked Ted if he has another servo. I’ve been itching to do this for a while now, and the itch has been cured…for now! Again this is very rough work, this is probably the first time you’ll ever see such a thing as a cardboard bracket with blutack hinges. But I plan to upgrade with sugru soon, if I manage to get my hands with some more of them. Anyway without further ado, Blue the blutack jointed robot(ps please watch videos in 720p or 1080p as it looks horrible on 480p):

20121107-003723.jpg

20121107-003752.jpg


//
// This program interfaces a Wii wii motion plus with the dsPIC using the I2C module
// Pan and tilt using two servos
// Program written by Gian Lorenzo
// Last edited 07/11/2012 1:39AM
//

#include <libpic30.h>
#include <p30f4011.h>
#include <stdio.h>
#include <xc.h>
#include <i2c.h>
#include <math.h>

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16);  // Fosc=16x7.5MHz, Fcy=30MHz
_FWDT(WDT_OFF);                   // Watchdog timer off
_FBORPOR(MCLR_DIS);               // Disable reset pin

// Function prototypes
void setup();								// Configure pins etc
unsigned int read_analog_channel(int n);	// Reading analog channel
void wiimp_init();						// wiimp initialisation/Handshake
void calibrate();
void wiimp_read(unsigned char*);			// Reading data from the Wii wiimp              // Nunchuk data array
void pause(unsigned int ms);				// Pause function(in ms)
void forward();								// Moves the Servo forward
void back();								// Moves the Servo backward

double yawzero=8054.7,pitchzero=7744,rollzero=7745,
yaw,roll,pitch, deltayaw, deltaroll,deltapitch;
unsigned char  wii_data[20],c; 


int main()
{
	unsigned int ax,ay,az;  // Data containers
	setup();				// Set up which pins are which and set up i2c and configs
	pause(1000);			// 1 second startup delay
	wiimp_init();			// Initialise wii motio plus
	pause(1000);
	//calibrate();			// Calibrate for 0
	pause(1000);
	int o = 0;
	
	while(1)
	{
		if(o == 0)	// Get rid of bad readings
		{
			wiimp_read(wii_data);                      
			wiimp_read(wii_data);
			wiimp_read(wii_data);
			wiimp_read(wii_data);
			wiimp_read(wii_data);
			wiimp_read(wii_data);
			wiimp_read(wii_data);
			pause(2000);
			o++;
		}
				
		wiimp_read(wii_data);                       // Read data from nunchuk

		ax = ((wii_data[3] & 0xFC) << 6) + wii_data[0];	// Extract gyroscope yaw byte
		ay = ((wii_data[4] & 0xFC) << 6) + wii_data[1];	// Extract gyroscope roll byte
		az = ((wii_data[5] & 0xFC) << 6) + wii_data[2];	// Extract gyroscope pitch byte
		
		yaw = ax - yawzero;
		roll = ay - rollzero;
		pitch = az - pitchzero;
		
		deltayaw = deltayaw + yaw;
		deltaroll = deltaroll + roll;
		deltapitch = deltapitch + pitch;
		
		
		if (U2STAbits.URXDA == 1)			//recalibration using uart
		{
			c = U2RXREG;
			if (c == 'y')	deltayaw = 0;
			else if(c == 'p')	deltapitch =0;
			else if(c =='r')	deltaroll =0;
			else if(c == 'a')	deltayaw =0;deltapitch=0;deltaroll=0;
		}
		PDC2 = 1410+(deltapitch*0.06083333333333/2);
		PDC1 = 1280-(deltayaw*.06083333333333/2);
		pause(20);
		//printf("%1d\r %1d\r %1d\r\r\n", ax, ay, az);
		printf("%1f\r %1f\r %1f\r\r\n", deltayaw, deltaroll, deltapitch);
	}

    return 0;
}
void forward()
{
	if(PDC1 < 2010)
	{
		PDC1 = PDC1 + 20;
		pause(.5);
	}
}
void back()
{
	if(PDC1 > 550)
	{
		PDC1 = PDC1 - 20;
		pause(.5);
	}
}

void setup()
{
    // Setup UART
	__C30_UART=2;			// Use secondary UART
    U2BRG = 48;            	// 38400 baud @ 30 MIPS
    U2MODEbits.UARTEN = 1; 	// Enable UART

    // Configure D ports as a digital outputs
    LATD = 0;
    TRISD = 0b11110000;

    // Configure analog inputs
    TRISB = 0x01FF;      /* Port B all inputs */
    ADPCFG = 0xFF00;     /* PORTB 0-7 are analog inputs */
    ADCON1 = 0;          /* Manually clear SAMP to end sampling, start conversion*/
    ADCON2 = 0;          /* Voltage reference from AVDD and AVSS */
    ADCON3 = 0x0005;     /* Manual Sample, ADCS=5 -> Tad = 3*Tcy = 0.1us */
    ADCON1bits.ADON = 1; /* Turn ADC ON */

    // Configure PWM
    // PWM period = Tcy * prescale * PTPER = 0.33ns * 64 * 9470 = 20ms
    PWMCON1 = 0x00FF; // Enable all PWM pairs in complementary mode
    PTCON = 0;
    _PTCKPS = 3;      // prescale=1:64 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
    PTPER = 9470;     // 20ms PWM period (15-bit period value)
    PDC1 = 0;         // 0% duty cycle on channel 1 (max is 65536)
    PDC2 = 0;         // 0% duty cycle on channel 2 (max is 65536)
    PDC3 = 0;         // 0% duty cycle on channel 3 (max is 65536)
    PTMR = 0;         // Clear 15-bit PWM timer counter
    _PTEN = 1;        // Enable PWM time base
	
	// Configure I2c
	I2CCONbits.I2CSIDL = 1;	// Stop module in idle mode
	I2CCONbits.A10M = 0;	// 7-bit address
	I2CCONbits.DISSLW = 0;	// slew rate control. Required to be on for 400kHZ
	I2CBRG = 272;			// 272 for 100kHz at FCY = 30MHz formula next line
							// I2CBRG = (((1/rate)-900ns)*FCY)-1
	I2CCONbits.I2CEN = 1;	// Enable i2c
}

void calibrate()
{
	int i=0,o=0;
    for (o=0;o<10;o++)
      {
        wiimp_read(wii_data);
		pause(10);
      }
	wiimp_read(wii_data);
    yawzero=((wii_data[3]>>6)+wii_data[0]);
    rollzero=((wii_data[4]>>6)+wii_data[1]);
    pitchzero=((wii_data[5]>>6)+wii_data[2]);
    for (i=0;i<10;i++)
      {
        wiimp_read(wii_data);
        yawzero=((wii_data[3]>>6)+wii_data[0])+yawzero/2; //average 10 readings
        rollzero=((wii_data[4]>>6)+wii_data[1])+rollzero/2;
        pitchzero=((wii_data[5]>>6)+wii_data[2])+rollzero/2;
      }
	printf("%f %f %f", yawzero,rollzero,pitchzero);
	pause(2000);
}
void wiimp_init(void) 
{
	StartI2C();			// Open I2C bus
	IdleI2C();			// Wait for last command to finish
	MasterWriteI2C(0xA6);// Call device adress 53(Wii wiimp)
	MasterWriteI2C(0xFE);// Handshake protocol for wii wiimp	
	MasterWriteI2C(0x04);
	StopI2C();
	printf("connected\r\n");
}

void wiimp_read(unsigned char* array) 
{
	int idx;	// Counter

	// ---- Request measurement ---- //
	StartI2C();		// Open I2C bus
	IdleI2C();		// Wait for last command to finish
	MasterWriteI2C(0xA4); // Call the adress you want to read from
	MasterWriteI2C(0x00);	
	StopI2C();		// Stop I2C
	pause(10); 	// 3 ms or more required or acquisition goes wrong

	 //Request reading
  	StartI2C();	// Open I2C bus
	IdleI2C();	// Wait for last command to finish
	MasterWriteI2C(0xA5);	// 0XA5 is the read adress of the wii wiimp(52+1)
	IdleI2C();	// Wait for last command to finish
	 //Bytes reception
	for(idx = 0; idx < 5; idx++)
	{
		array[idx] = MasterReadI2C(); 	// Store the values into an array
		AckI2C(); 						// Send an ACK to notify wiimp to send next byte
		IdleI2C();						// Wait for last command to finish
	}
	pause(1);
	array[5] = MasterReadI2C(); 		// Last byte to be read
	NotAckI2C(); IdleI2C();				// Send a NACK to tell wiimp that reading is done
	StopI2C();							// Stop I2C
}

// Analogue input reading
unsigned int read_analog_channel(int channel)
{
    ADCHS = channel;          // Select the requested channel
    ADCON1bits.SAMP = 1;      // start sampling
    __delay32(30);            // 1us delay @ 30 MIPS
    ADCON1bits.SAMP = 0;      // start Converting
    while (!ADCON1bits.DONE); // Should take 12 * Tad = 1.2us
    return ADCBUF0;
}
void pause(unsigned int ms)
{
	__delay32(30000L*ms);	// 1ms Pause = (ms * FCY/1000)
}

Interfacing a Wii motion plus to the dspic30F4011

I’m at it again!

And so after my ventures with I2C and the Wii nunchuck; many prospects of the “mini project” have opened up. While the Wii nunchuck’s accelerometer is great, there’s no harm in trying out new sensors. So far I can think of two applications but they will require a lot of time and work(and money!) to build and so I’ll think about it more. The first one is sort of a crane attached onto a “roof”, Two stepper motors control the x and y coordinates of the plane and the Wii motion plus can control the movement of the actual gripper. The second one is a robot car with a crane on in and works pretty much the same as the one I said there but with wheels and is on the ground. I’m also intrigued into building something aerial but I don’t want to get too ahead of myself.

And so I got my Wii motion plus that’s lying around and got to work into trying to communicate with it. The way it communicates is also through I2C and the code I came up with is pretty much the same as with the nunchuck, only this time around I’m only reading three values as the Wii motion plus is basically a gyroscope.  I’ve got to admit I’m liking this sensor more than the nunchuck, so far anyway. I really wish I had another servo right now, as I can only utilise one axis with one servo. It’s good enough for the demo I suppose.

Gyroscope

So, what makes the wii motion plus sensor work? That could be answered in one word: A gyroscope(ok that’s two ha ha Gian can’t count). But to explain how a gyroscope works can be a bit tricky and I wont even try to explain it too deeply. The image above is one of a toy gyroscope, when I first played with one as a child I was dumbfounded and no one around me could explain how it does what it does. It’s basically constructed with a spinning wheel in the middle and round metal things around it. Without doing anything to it; it does nothing, it’s just a piece of metal. But spin the wheel and the middle and the magic happens, when the wheel is spun it seems to defy gravity as it can support itself sideways and in awkward positions and not lose its orientation. The reason behind this is because the wheel in the middle has angular velocity and will try to repel any force being applied to it. I think this video will explain in better more than I can:

So now you know what a gyroscope does. This little sensor of mine has one inside it and so it will tell me the orientation of my device in 3 axes which are the yaw angle, the roll angle, and the pitch angle. When the orientation is changed: the gyroscope will repel it and this degree of repulsion is sent down as a signal. This can be very useful indeed, so useful that every flying machine nowadays uses one to know its orientation.

Only downside is: it doesn’t know it’s initial orientation. This is the reason why everytime you want to use your wii motion plus on the console you have to put it down a flat surface before you can use it. But A-ha! We have the wii nunchuck which has an accelerometer and will tell us the current pitch of the device and so it’s a great match!

Controlling a servo using the “yaw” angle

As I’ve said I only have one servo at the moment sadly and so I had to choose one variable to use as my “controller”. I used the yaw angle randomly, this is basically the angle that goes “side-to-side” and I just scaled it to the range of the servo. That’s all that there is! Simple as. Now to talk about the I2C communication, and as I’ve said: it’s nearly the same as the wii nunchuck one, only that this sensor has a different I2C address(0x53) when trying to initialise and then it becomes 0x52 or A4 once initialised. This will cause a bit of trouble when both the Wii motion sensor and the nunchuck are being used. But there is a way of deactivating the wii motion plus with code and look at the nunchuck data, but that’s for another post. Six bytes are read from the Wii motion plus and they are as follows:

It can be seen that there are actually two modes in which the sensor operates. One is the slow mode and the other is the fast mode, this is basically when you try and change the orientation quickly or slowly, they will both have different multipliers(4 and 20) and so it has to be taken into account. And another thing is that the data is actually moved 6 bits in and so we have to read from “wii_data[x]>>6” for example. Plenty of useful stuff here if anyone is interested. And without further ado, here’s my short demo:


//
// This program interfaces a Wii wii motion plus with the dsPIC using the I2C module
//
// Program written by Gian Lorenzo
// Last edited 05/11/2012 10:26AM
//

#include <libpic30.h>
#include <p30f4011.h>
#include <stdio.h>
#include <xc.h>
#include <i2c.h>
#include <math.h>

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16);  // Fosc=16x7.5MHz, Fcy=30MHz
_FWDT(WDT_OFF);                   // Watchdog timer off
_FBORPOR(MCLR_DIS);               // Disable reset pin

// Function prototypes
void setup();								// Configure pins etc
unsigned int read_analog_channel(int n);	// Reading analog channel
void wiimp_init();						// wiimp initialisation/Handshake
void calibrate();
void wiimp_read(unsigned char*);			// Reading data from the Wii wiimp              // Nunchuk data array
void pause(unsigned int ms);				// Pause function(in ms)
void forward();								// Moves the Servo forward
void back();								// Moves the Servo backward

double yawzero=8055.5,pitchzero=7612,rollzero=7739,
yaw,roll,pitch, deltayaw, deltaroll,deltapitch;
unsigned char  wii_data[20];

int main()
{
	unsigned int ax,ay,az;  // Data containers
	setup();				// Set up which pins are which and set up i2c and configs
	pause(1000);			// 1 second startup delay
	wiimp_init();			// Initialise wii motio plus
	pause(1000);
	//calibrate();			// Calibrate for 0
	pause(1000);
	int o = 0;

	while(1)
	{
		if(o == 0)	// Get rid of bad readings
		{
			wiimp_read(wii_data);
			wiimp_read(wii_data);
			wiimp_read(wii_data);
			wiimp_read(wii_data);
			wiimp_read(wii_data);
			wiimp_read(wii_data);
			wiimp_read(wii_data);
			pause(1000);
			o++;
		}

		wiimp_read(wii_data);                       // Read data from nunchuk

		ax = ((wii_data[3] & 0xFC) << 6) + wii_data[0];	// Extract gyroscope yaw byte
		ay = ((wii_data[4] & 0xFC) << 6) + wii_data[1];	// Extract gyroscope roll byte
		az = ((wii_data[5] & 0xFC) << 6) + wii_data[2];	// Extract gyroscope pitch byte

		yaw = ax - yawzero;
		roll = ay - rollzero;
		pitch = az - pitchzero;

		deltayaw = deltayaw + yaw;
		deltaroll = deltaroll + roll;
		deltapitch = deltapitch + pitch;
		// Extract gyroscope pitch byte
		PDC1 = 1280-(deltayaw*.06083333333333);
		pause(50);
		//printf("%1d\r %1d\r %1d\r\r\n", ax, ay, az);
		printf("%1f\r %1f\r %1f\r\r\n", deltayaw, deltaroll, deltapitch);
	}

    return 0;
}
void forward()
{
	if(PDC1 < 2010) 	{ 		PDC1 = PDC1 + 20; 		pause(.5); 	} } void back() { 	if(PDC1 > 550)
	{
		PDC1 = PDC1 - 20;
		pause(.5);
	}
}

void setup()
{
    // Setup UART
	__C30_UART=2;			// Use secondary UART
    U2BRG = 48;            	// 38400 baud @ 30 MIPS
    U2MODEbits.UARTEN = 1; 	// Enable UART

    // Configure D ports as a digital outputs
    LATD = 0;
    TRISD = 0b11110000;

    // Configure analog inputs
    TRISB = 0x01FF;      /* Port B all inputs */
    ADPCFG = 0xFF00;     /* PORTB 0-7 are analog inputs */
    ADCON1 = 0;          /* Manually clear SAMP to end sampling, start conversion*/
    ADCON2 = 0;          /* Voltage reference from AVDD and AVSS */
    ADCON3 = 0x0005;     /* Manual Sample, ADCS=5 -> Tad = 3*Tcy = 0.1us */
    ADCON1bits.ADON = 1; /* Turn ADC ON */

    // Configure PWM
    // PWM period = Tcy * prescale * PTPER = 0.33ns * 64 * 9470 = 20ms
    PWMCON1 = 0x00FF; // Enable all PWM pairs in complementary mode
    PTCON = 0;
    _PTCKPS = 3;      // prescale=1:64 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
    PTPER = 9470;     // 20ms PWM period (15-bit period value)
    PDC1 = 0;         // 0% duty cycle on channel 1 (max is 65536)
    PDC2 = 0;         // 0% duty cycle on channel 2 (max is 65536)
    PDC3 = 0;         // 0% duty cycle on channel 3 (max is 65536)
    PTMR = 0;         // Clear 15-bit PWM timer counter
    _PTEN = 1;        // Enable PWM time base

	// Configure I2c
	I2CCONbits.I2CSIDL = 1;	// Stop module in idle mode
	I2CCONbits.A10M = 0;	// 7-bit address
	I2CCONbits.DISSLW = 0;	// slew rate control. Required to be on for 400kHZ
	I2CBRG = 272;			// 272 for 100kHz at FCY = 30MHz formula next line
							// I2CBRG = (((1/rate)-900ns)*FCY)-1
	I2CCONbits.I2CEN = 1;	// Enable i2c
}

void calibrate()
{
	int i=0,o=0;
    for (o=0;o<10;o++)       {         wiimp_read(wii_data); 		pause(10);       } 	wiimp_read(wii_data);     yawzero=((wii_data[3]>>6)+wii_data[0]);
    rollzero=((wii_data[4]>>6)+wii_data[1]);
    pitchzero=((wii_data[5]>>6)+wii_data[2]);
    for (i=0;i<10;i++)       {         wiimp_read(wii_data);         yawzero=((wii_data[3]>>6)+wii_data[0])+yawzero/2; //average 10 readings
        rollzero=((wii_data[4]>>6)+wii_data[1])+rollzero/2;
        pitchzero=((wii_data[5]>>6)+wii_data[2])+rollzero/2;
      }
	printf("%f %f %f", yawzero,rollzero,pitchzero);
	pause(2000);
}
void wiimp_init(void)
{
	StartI2C();			// Open I2C bus
	IdleI2C();			// Wait for last command to finish
	MasterWriteI2C(0xA6);// Call device adress 53(Wii wiimp)
	MasterWriteI2C(0xFE);// Handshake protocol for wii wiimp
	MasterWriteI2C(0x04);
	StopI2C();
	printf("connected\r\n");
}

void wiimp_read(unsigned char* array)
{
	int idx;	// Counter

	// ---- Request measurement ---- //
	StartI2C();		// Open I2C bus
	IdleI2C();		// Wait for last command to finish
	MasterWriteI2C(0xA4); // Call the adress you want to read from
	MasterWriteI2C(0x00);
	StopI2C();		// Stop I2C
	pause(10); 	// 3 ms or more required or acquisition goes wrong

	 //Request reading
  	StartI2C();	// Open I2C bus
	IdleI2C();	// Wait for last command to finish
	MasterWriteI2C(0xA5);	// 0XA5 is the read adress of the wii wiimp(52+1)
	IdleI2C();	// Wait for last command to finish
	 //Bytes reception
	for(idx = 0; idx < 5; idx++)
	{
		array[idx] = MasterReadI2C(); 	// Store the values into an array
		AckI2C(); 						// Send an ACK to notify wiimp to send next byte
		IdleI2C();						// Wait for last command to finish
	}
	pause(1);
	array[5] = MasterReadI2C(); 		// Last byte to be read
	NotAckI2C(); IdleI2C();				// Send a NACK to tell wiimp that reading is done
	StopI2C();							// Stop I2C
}

// Analogue input reading
unsigned int read_analog_channel(int channel)
{
    ADCHS = channel;          // Select the requested channel
    ADCON1bits.SAMP = 1;      // start sampling
    __delay32(30);            // 1us delay @ 30 MIPS
    ADCON1bits.SAMP = 0;      // start Converting
    while (!ADCON1bits.DONE); // Should take 12 * Tad = 1.2us
    return ADCBUF0;
}
void pause(unsigned int ms)
{
	__delay32(30000L*ms);	// 1ms Pause = (ms * FCY/1000)
}

Interfacing a Wii nunchuck with a dsPIC30f4011 using I2C(about time!)

Finally…

Hey folks, after a whole month of tears, blood and sweat – OK I apologize it wasn’t that bad, but it was close! – of trying(and trying and trying…) to interface a Nintendo Wii nunchuck with a dsPIC30f4011, I can proudly say that finally, I have succeeded. This little side project of mine is the reason why I haven’t posted anything substantial in this blog for quite a while. While everyone is flying away with their servos and steppers I’m somewhere trying to figure out what I was doing wrong. But the sleepless nights were all worth it just to see that UART console display something else aside from “0 0 255 255 255 255 255” (T.T). But nevermind my silly attachments to the piece of plastic, let’s talk I2C(or maybe not I don’t know how far I’ll get tonight). The demonstration I’ll have on this post will be using a Wii nunchuck to control a servo motor, when the ‘C’ button is pressed – the servo can be controlled using the analog stick of the nunchuck, and when the ‘Z’ button is pressed – the accelerometer is utilised and is used to control the movement of the servo, only one axis is used for this demo, the x axis in particular. I was going to use at least two but I don’t have another servo to do it, I spent last night trying to use the stepper as a second joint, but had problems as the driver chip used to control the servo needed more juice(the pic voltage had to be set low for the Wii nunchuck – more of that later). Now, time for things to get messy:

The Nintendo Wii Nunchuck

You’ve seen it at least once in your life if there’s a child in your household(that would be me in our household). The Nintendo Wii nunchuck is a very cheap and affordable sensor to use(€14 on ebay for the official one, and around €7 for the knockoff one), specially since it has an accelerometer inside that costs a bit more than your conventional sensor. The Wii sensor has two buttons C and Z on the top, which are basically just push buttons, an analog stick which are just two potentiometers, and as I’ve said above – an accelerometer. The only catch with it is the way it communicates – I2C. And luckily enough the dsPIC has it’s own I2C module that I can utilise.

The Accelerometer

‘So what is an accelerometer?’ one might ask. In a nutshell an accelerometer is a device that measures – yes you guessed it – acceleration. This can be static or dynamic acceleration, static being the acceleration due to the earths gravity, and dynamic when one actually moves the device and cause vibrations. By measuring the static acceleration one can find out how much an object is tilted, and the dynamic one measures how much an object is accelerating due to actual movement. The Wii nunchucks accelerometer supplies us with 3 axis data types – the X, Y, and Z planes.

As it can be seen the actual sensor is tiny, but in reality it’s much smaller than that as what can be seen is the casing around it used to protect the very sensitive insides. There’s a few ways on how an accelerometer operates: it could be by changing capacitance, heat transfer, piezoelectric – and the list goes on. Surprisingly, an accelerometer has moving parts(the wii nunchucks one anyway), the way it works is that by moving it or creating vibrations causes tiny “springs” inside the accelerometer to bounce around its walls and varies the overall capacitance of the sensor, which in turn gives us a signal when voltage is passed through it. I’ll go more in depth on this topic later on if I decide to go this route(which is very likely), but for now we can just accept the fact that an accelerometer gives us the orientation of the Wii nunchuck and tells us when it moves in space(no one can hear you scream, sorry I just can’t help it!).

The I2C Module

As mentioned above; the method that the nunchuck communicates is through ‘I2C’. I2C is one of the communications that’s available with the dsPIC30f4011. The most common communication we have used in class(the only one I think actually) is the UART module. There are other types available with the dsPIC such as CAN, but I2C is the only option when it comes to communicating with the nunchuck since this is how it was originally built and programmed, and this is the way the actual Wii console communicates with it. I will try to go through as much as I’ve learned by myself in explaining the I2C module but forgive me if I make any false statements as I am but a noob when it comes to this way of communication. Oh and if anyone has noticed, the SDA and SCL pins are actually using the same pins as the programming pins of the dsPIC30f4011 which is also the UART RX and TX pins and so it has to be removed everytime you want to program it. UART on the other hand; can still be used, this can be done by sending the data to the secondary uart pin set “U2TX” and “U2RX” which are pins 27 and 28 respectively. All one has to do is use this secondary UART and the communication can still be successful, this is useful for people who want to use the USB to Serial dongle that came in the ziploc bag. Instead of the normal UART configuration you put this instead:

	__C30_UART=2;      // Set the secondary uart as the primary
    U2BRG = 48;            // 38400 baud @ 30 MIPS
    U2MODEbits.UARTEN = 1; // Enable UART 2

After doing so all you have to do is move the U1RX pin connection to the U2RX pin and move the U1TX pin to the U2TX pin and proceed as you would with using the UART normally.

I2C Baud Rate Generator

A way of setting the clock frequency of the I2C module is by changing the I2CBRG value in the register. The clock frequency is very important as this determines how fast you’re going to read from the nunchuck. And believe you me it is very temperamental. If you try to read from the nunchuck too often and too quickly – it will give bad readings which is a no no. This is the main culprit on why I spent so long trying to get it to work, I still cringe everytime I think about it. In the end I set the Frequency to 100kHz and everything worked as it should. The formula for calculating the I2CBRG is on page 110 of the dsPIC30f4011 data sheet or you can just look below. My value was calculated at 272 but I’ve been trying to push it ever since and so I’m using 75 now without any issues(so far anyway). My FCY is set to 30MIPS.

The Master and Margarita….and the slave

The I2C consists of only 2 pins: the SDA or data line, and SCL or clock line which are pins 26 and 25 respectively on the dsPIC30f4011. The data line is – surprise surprise – responsible for sending the data while the clock line controls the data flow. The amazing thing about I2C is that you can virtually hook up any number of devices, which will act as “slaves”, on the bus and call on them by just calling out their “adresses” which I’ll go into more detail in a bit. There’s two types of devices connected on the I2C bus – the Master, and the slave. The master is the one in charge at that certain point in time and can initiate a “Start” sequence and communicate with any slave that it wishes – so long that it knows its address. The master can transmit and receive data whilst the slave can only do whatever the master tells it to – befitting of its title. In my setup on this demo, the dsPIC acts as a master and the nunchuck will act as the slave. If the master calls the address of a slave it can ask for its contents in byte chunks. The wii nunchuck only has 6 usable byte chunks which contains the information that we need. Below is the table which shows what these bytes contain:

Everytime a byte is received by the Master an ACK or a positive acknowledge or a high pin is sent to the slave to tell it to move to the next byte, when all the bytes have been read – an NACK or a negative acknowledge is sent or a low pin to tell the slave that transmission is done and that he is no longer needed.

Nunchuck special handshake and I2C read sequence

Nintendo is a great company, I consider myself a big fanboy(what I’d give to shake Shigeru Miyamoto’s hand). They’re always the first to innovate when it comes with the video game industry, they have a hold of great intellectual property, and it’s expected that they protect this. Why am I rambling about this you ask? Well because of the fact that the Wii nunchuck are one of these guarded(not well enough) pieces of technology. The data is encrypted and the nunchuck wouldn’t respond if you tried to talk to it using the conventional I2C sequence which is:

  • (As master talking to a slave) Open the I2C bus
  • Call out the slave’s address and send 0
  • Close I2C bus
  • (If data is to be acquired)Open bus again and call out the address once more then send 0 and then close it again
  • Request the data by talking to the “Read address” of the device which is “Write address + 1”
  • Close bus once transmission is done

This will not work with the Wii nunchuck, even though we know its write address(which is 52 by the way, A4 in hex). For the communication to work – after sending the write adress for the first time – “0x40” must be sent before 0.

	StartI2C();
	IdleI2C();
	MasterWriteI2C(0xA4);
	MasterWriteI2C(0x40);
	MasterWriteI2C(0x00);
	StopI2C();

And again for the knockoff versions of the nunchuck it’s different once more, there are not only one but TWO handshakes this time around, the cheeky bastards:

	StartI2C();
	IdleI2C();
	MasterWriteI2C(0xA4);
	MasterWriteI2C(0xF0);
	MasterWriteI2C(0x55);
	StopI2C();

	StartI2C();
	IdleI2C();
	MasterWriteI2C(0xA4);
	MasterWriteI2C(0xFB);
	MasterWriteI2C(0x00);
	StopI2C();

Just from looking up and reading around I’ve found out these information, here are the ones that I think are useful as these are the settings that I used experimenting around:

  1. Wii nunchuck write address = 52 or A4 in hex
  2. Wii nunchuck read address = 53 or A5 in hex
  3. Wii nunchuck operating frequency = 100khz normally but has been known to work up to 400khz as long as the slew rate control is turned on.
  4. Has the special handshake A4 – 40 – 00 for the original and A4 – F0 – 55 – A4 – FB – 00 for the knockoff ones.
  5. Takes in 3.3V as an input but was resetting when testing at this power level so I’m working mine with 3.8V.

Taking it out for a stroll with a Servo motor

20121105-003329.jpg

20121105-003355.jpg

With the hard part out of the way, I was finally able to test out my nunchuck. It took me a month just to accomplish my first goal which was to turn an LED on when I press the Z button! Without further ado: here’s my simple test.

Now I know it’s pretty basic but I worked with what I had, If I had another servo I would have been more “inspired” and do a pan and tilt system – which I plan to do in the near future when I get my hands on another servo. As I said earlier I tried to incorporate the stepper into it as well, using it to act like a servo, but alas I had to power down my pic to 3.8V to accomodate the Wii nunchuck. The digital signals out weren’t enough to control the motor drivers and so all I got off the stepper motor was a slight vibration. I had the code all figured out and everything! But this will have to do for now, very basic, x axis controls the servo. Pressing “C” takes you to analog stick mode and Z button takes you to Accelerometer mode. A thing that I found out while experimenting on it was that the values that the nunchuck is giving out isn’t linear. Not linear as in: for the x axis accelerometer the figures given were around 180, in midband it was around 500, and fully tilted right was 590. I still have to do a bit more reading so watch out for a better demo in the near future. For now I’m just happy that it works. Below is the wiring diagram and the code, I don’t think I need to explain it as it’s very simple and the complicated parts: I already explained on the top. In a nutshell I’m taking the 6 Byte values, storing them in an array and manipulating it. That is all I can think for now, I’ll add in more when I think I missed anything.


//
// This program interfaces a Wii nunchuck with the dsPIC using the I2C module
//
// Program written by Gian Lorenzo
// Last edited 03/11/2012 1:26AM
//

#include <libpic30.h>
#include <p30f4011.h>
#include <stdio.h>
#include <xc.h>
#include <i2c.h>

#define Scale(a) ((a^0x17)+0x17)

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16);  // Fosc=16x7.5MHz, Fcy=30MHz
_FWDT(WDT_OFF);                   // Watchdog timer off
_FBORPOR(MCLR_DIS);               // Disable reset pin

// Function prototypes
void setup();								// Configure pins etc
unsigned int read_analog_channel(int n);	// Reading analog channel
void nunchuck_init();						// Nunchuck initialisation/Handshake
void nunchuck_read(unsigned char*);			// Reading data from the Wii nunchuck
unsigned char  wii_data[20];                // Nunchuk data array
void pause(unsigned int ms);				// Pause function(in ms)
void forward();								// Moves the Servo forward
void back();								// Moves the Servo backward

int main()
{
	unsigned int  Z_btn, C_btn, jx,jy,ax,ay,az;                 // Buttons on nunchuk
	pause(2000);
	setup();				// Set up which pins are which and set up i2c and configs
	pause(1000);			// 1 second startup delay
	nunchuck_init();		// Initialise nunchuck

	while(1)
	{
		nunchuck_read(wii_data);                       // Read data from nunchuk
		C_btn = 0;
		Z_btn = (wii_data[5] >> 0 & 1) ^ 0b1;              // Extract Z button bit
		if((((wii_data[5] >> 1 & 1) ^ 0b1) == 1 &&
		((wii_data[5] >> 0 & 1) ^ 0b1) == 0) ||
		(((wii_data[5] >> 1 & 1) ^ 0b1) == 0 &&
		((wii_data[5] >> 0 & 1) ^ 0b1) == 1)) C_btn =1;		// Extract C button bit
		jx = wii_data[0];								// Extract joystick x byte
		jy = wii_data[1];								// Extract joystick y byte
		ax = wii_data[2];								// Extract accelerometer x byte
		ay = wii_data[3];								// Extract accelerometer y byte
		az = wii_data[4];								// Extract accelerometer z byte
		//if ((wii_data[5] >> 2) & 1)  ax += 2;
		//if ((wii_data[5] >> 3) & 1)  ax += 1;
		//if ((wii_data[5] >> 4) & 1)  ay += 2;
		//if ((wii_data[5] >> 5) & 1)  ay += 1;
		//if ((wii_data[5] >> 6) & 1)  az += 2;
		//if ((wii_data[5] >> 7) & 1)  az += 1;
		pause(1);
		printf("%1d\r %1d\r %3d\r %3d\r %3d\r %3d\r %3d\r\n", //\r\n
		Z_btn, C_btn, jx, jy, ax, ay, az);

		_LATD0 = 0;
		_LATD2 = 0;
		if(C_btn)
		{
			_LATD2 = 1;
			pause(10);
			PDC1 = (550 + (8.1*jx) - 162);
		}
		if(Z_btn)
		{
			_LATD0 = 1;
			pause(1);
			if(ax<100)	back();
 			else if(ax>130)	forward();
		}
	}

    return 0;
}
void forward()
{
	if(PDC1 < 2010) 	{ 		PDC1 = PDC1 + 20; 		pause(.5); 	} } void back() { 	if(PDC1 > 550)
	{
		PDC1 = PDC1 - 20;
		pause(.5);
	}
}

void setup()
{
    // Setup UART
	__C30_UART=2;			// Use secondary UART
    U2BRG = 48;            	// 38400 baud @ 30 MIPS
    U2MODEbits.UARTEN = 1; 	// Enable UART

    // Configure D ports as a digital outputs
    LATD = 0;
    TRISD = 0b11110000;

    // Configure analog inputs
    TRISB = 0x01FF;      /* Port B all inputs */
    ADPCFG = 0xFF00;     /* PORTB 0-7 are analog inputs */
    ADCON1 = 0;          /* Manually clear SAMP to end sampling, start conversion*/
    ADCON2 = 0;          /* Voltage reference from AVDD and AVSS */
    ADCON3 = 0x0005;     /* Manual Sample, ADCS=5 -> Tad = 3*Tcy = 0.1us */
    ADCON1bits.ADON = 1; /* Turn ADC ON */

    // Configure PWM
    // PWM period = Tcy * prescale * PTPER = 0.33ns * 64 * 9470 = 20ms
    PWMCON1 = 0x00FF; // Enable all PWM pairs in complementary mode
    PTCON = 0;
    _PTCKPS = 3;      // prescale=1:64 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
    PTPER = 9470;     // 20ms PWM period (15-bit period value)
    PDC1 = 0;         // 0% duty cycle on channel 1 (max is 65536)
    PDC2 = 0;         // 0% duty cycle on channel 2 (max is 65536)
    PDC3 = 0;         // 0% duty cycle on channel 3 (max is 65536)
    PTMR = 0;         // Clear 15-bit PWM timer counter
    _PTEN = 1;        // Enable PWM time base

	// Configure I2c
	I2CCONbits.I2CSIDL = 1;	// Stop module in idle mode
	I2CCONbits.A10M = 0;	// 7-bit address
	I2CCONbits.DISSLW = 0;	// slew rate control. Required to be on for 400kHZ
	I2CBRG = 272;			// 272 for 100kHz at FCY = 30MHz formula next line
							// I2CBRG = (((1/rate)-900ns)*FCY)-1
	I2CCONbits.I2CEN = 1;	// Enable i2c
}

void nunchuck_init(void)
{
	StartI2C();			// Open I2C bus
	IdleI2C();			// Wait for last command to finish
	MasterWriteI2C(0xA4);// Call device adress 52(Wii nunchuck)
	MasterWriteI2C(0x40);// Handshake protocol for wii nunchuck
	MasterWriteI2C(0x00);
	StopI2C();
	printf("connected\r\n");
}

void nunchuck_read(unsigned char* array)
{
	int idx;	// Counter

	// ---- Request measurement ---- //
	StartI2C();		// Open I2C bus
	IdleI2C();		// Wait for last command to finish
	MasterWriteI2C(0xA4); // Call the adress you want to read from
	MasterWriteI2C(0x00);
	StopI2C();		// Stop I2C
	pause(5); 	// 3 ms or more required or acquisition goes wrong

	// Request reading
  	StartI2C();	// Open I2C bus
	IdleI2C();	// Wait for last command to finish
	MasterWriteI2C(0xA5);	// 0XA5 is the read adress of the wii nunchuck(52+1)
	IdleI2C();	// Wait for last command to finish
	// Bytes reception
	for(idx = 0; idx < 5; idx++)
	{
		array[idx] = MasterReadI2C(); 	// Store the values into an array
		AckI2C(); 						// Send an ACK to notify nunchuck to send next byte
		IdleI2C();						// Wait for last command to finish
	}
	pause(1);
	array[5] = MasterReadI2C(); 		// Last byte to be read
	NotAckI2C(); IdleI2C();				// Send a NACK to tell nunchuck that reading is done
	StopI2C();							// Stop I2C
}

// Analogue input reading
unsigned int read_analog_channel(int channel)
{
    ADCHS = channel;          // Select the requested channel
    ADCON1bits.SAMP = 1;      // start sampling
    __delay32(30);            // 1us delay @ 30 MIPS
    ADCON1bits.SAMP = 0;      // start Converting
    while (!ADCON1bits.DONE); // Should take 12 * Tad = 1.2us
    return ADCBUF0;
}
void pause(unsigned int ms)
{
	__delay32(30000L*ms);	// 1ms Pause = (ms * FCY/1000)
}

A little sneak peek on what I want to experiment with next(and the piezo electric disks!), So many sensors; so little time.20121105-004245.jpg

Analog voltmeter using stepper motor and a Colour sensor(Draft)

 

Wiring Diagram

20121029-221000.jpg

20121029-220952.jpg

20121029-220944.jpg

Very simple use of a stepper motor, will update this post in a bit. The video seems to stop prematurely around 2/3 of the video on my laptop for some reason so just click on the tracker bar to watch the rest of it.


//
// dsPIC30F4011 Stepper motor powered analog Voltmeter
// Written by Gian Lorenzo
// Last updated 25-10-2012
//

#include <xc.h>
#include <libpic30.h>
#include <stdio.h>
#include <p30f4011.h>
#include <i2c.h>

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, Fcy=30MHz
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin

// Function prototypes
void rotate(double degrees);	//	Rotate function
void step_forward();			//	Step forward function
void step_back();				// Step backward function
void setup();					// Setup configurations
unsigned int read_analog_channel(int channel);	// Read analog channel function
void pause(unsigned long ms);	// Pause in ms = (time in ms)*FCY/1000

// Global variable declarations
int t;
int voltage;
double svoltage;
double rotor_angle;
double step_angle;
long step_time;

int main()
{
	// Setup configurations
	setup();

	// Use initial position as angle origin
	rotor_angle = 0;

	// Stepper properties
	step_time = 120000L; // 4ms
	step_angle = 0.17578125; // 360.0 divided by 2048

	// Now rotate the stepper 6 degrees once every second
	while(1)
	{
		while(read_analog_channel(1)<500)
		{
			step_back();
		}
		rotor_angle = 0;
		while(1)
		{
			voltage = read_analog_channel(0);
			svoltage = voltage*(5/1023);
			if(rotor_angle<(voltage*step_angle)) 			{ 				step_forward(); 				printf("Voltage = %3f\n",svoltage); 			} 			else if(rotor_angle>(voltage*step_angle))
			{
				step_back();
			}
		}
	}

	return 0;
}

void rotate(double degrees)
{
	// The target angle is the current angle
	// plus the requested angle of rotation.
	double target_angle = rotor_angle + degrees;

	// Now keep stepping in the appropriate
	// direction until the target is reached.
	if (degrees > 0)
	{
		while(rotor_angle < target_angle) step_forward(); 	} 	else 	{ 		while(rotor_angle > target_angle) step_back();
	}
}

void step_forward()
{
	// Turn off the previous phase and turn
	// on the next one.
	if (LATD == 0b0001) LATD = 0b0010;
	else if (LATD == 0b0010) LATD = 0b0100;
	else if (LATD == 0b0100) LATD = 0b1000;
	else if (LATD == 0b1000) LATD = 0b0001;
	else LATD = 0b0001;

	// Increment the global angle tracker.
	rotor_angle = rotor_angle + step_angle;

	// Delay to allow rotor to move.
	__delay32(step_time);
}

void step_back()
{
	// Turn off the previous phase and turn
	// on the next one.
	if (LATD == 0b0001) LATD = 0b1000;
	else if (LATD == 0b0010) LATD = 0b0001;
	else if (LATD == 0b0100) LATD = 0b0010;
	else if (LATD == 0b1000) LATD = 0b0100;
	else LATD = 0b0001;

	// Decrement the global angle tracker.
	rotor_angle = rotor_angle - step_angle;

	// Delay to allow rotor to move.
	__delay32(step_time);
}
void setup()
{
    // Setup UART
	__C30_UART=2;
    U2BRG = 48;            // 38400 baud @ 30 MIPS
    U2MODEbits.UARTEN = 1; // Enable UART

    // Configure D ports as a digital outputs
    LATD = 0;
    TRISD = 0b11110000;

    // Configure analog inputs
    TRISB = 0x01FF;      /* Port B all inputs */
    ADPCFG = 0xFF00;     /* PORTB 0-7 are analog inputs */
    ADCON1 = 0;          /* Manually clear SAMP to end sampling, start conversion*/
    ADCON2 = 0;          /* Voltage reference from AVDD and AVSS */
    ADCON3 = 0x0005;     /* Manual Sample, ADCS=5 -> Tad = 3*Tcy = 0.1us */
    ADCON1bits.ADON = 1; /* Turn ADC ON */

    // Configure PWM
    // PWM period = Tcy * prescale * PTPER = 0.33ns * 64 * 9470 = 20ms
    PWMCON1 = 0x00FF; // Enable all PWM pairs in complementary mode
    PTCON = 0;
    _PTCKPS = 3;      // prescale=1:64 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
    PTPER = 9470;     // 20ms PWM period (15-bit period value)
    PDC1 = 0;         // 0% duty cycle on channel 1 (max is 65536)
    PDC2 = 0;         // 0% duty cycle on channel 2 (max is 65536)
    PDC3 = 0;         // 0% duty cycle on channel 3 (max is 65536)
    PTMR = 0;         // Clear 15-bit PWM timer counter
    _PTEN = 1;        // Enable PWM time base

	/*// Configure I2c
	TRISFbits.TRISF2 = 1; // SDA
	TRISFbits.TRISF3 = 1; // SCL
	I2CCONbits.I2CSIDL = 1;	// Stop module in idle mode
	I2CCONbits.A10M = 0;	// 7-bit address
	I2CCONbits.DISSLW = 0;	// slew rate control for 400khz must be on
	I2CBRG = 45;		// 272 for 100kHz at FCY = 30MHz formula next line
							// I2CBRG = ((1/rate - 250ns)*FCY)-1
	I2CCONbits.I2CEN = 1;	// Enable i2c*/
}

unsigned int read_analog_channel(int channel)
{
    ADCHS = channel;          // Select the requested channel
    ADCON1bits.SAMP = 1;      // start sampling
    __delay32(30);            // 1us delay @ 30 MIPS
    ADCON1bits.SAMP = 0;      // start Converting
    while (!ADCON1bits.DONE); // Should take 12 * Tad = 1.2us
    return ADCBUF0;
}
void pause(unsigned long ms)
{
	__delay32(ms*30000L);
}

2D mapping using servo and Sharp rangefinder(draft)

Wiring Diagram

20121029-220138.jpg

20121029-220108.jpg

Watch in HD to see my teeny text(Sorry!). Also it gave values of 0 on the 3:56 mark because one of the wires came loose.


// Basic 2D mapping using a servo motor and Sharp rangefinder
// Program written by Gian Lorenzo
// Last edited 29/10/2012
//

#include <libpic30.h>
#include <p30f4011.h>
#include <stdio.h>
#include <xc.h>
#include <i2c.h>

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16);  // Fosc=16x7.5MHz, Fcy=30MHz
_FWDT(WDT_OFF);                   // Watchdog timer off
_FBORPOR(MCLR_DIS);               // Disable reset pin

// Function prototypes
void setup();
unsigned int read_analog_channel(int n);
void pause(unsigned long ms);
void forward();
void back();
void forwardnum(unsigned int pdc);
void backnum(unsigned int pdc2);

int main()
{
	setup();
	int voltage;
	int status,x=1,y=1,a=0,b=1,c=0;
	int distance[36];
	int quadrant;
	int angle[5];
	int mag[5];
	int min_angle = 750, max_angle = 2230;
	
	PDC1 = min_angle;
	pause(1000);
	distance[0]=0;
	status=0;
	
	while(1)
	{
		voltage = read_analog_channel(0);
		while(x<35)
		{
			voltage = read_analog_channel(0);
			forwardnum(43);
			pause(300);
			if(voltage < 250)
			{
				distance[x]=0;
				status = 0;
			}
			else
			{
				distance[x]=voltage;
				status =1;
			}
			if(status == 1)
			{
				angle[b]=(x*5);
				mag[b]=distance[x-2];
				c=1;
				a++;
			}
			if(status == 0 && c==1 || (x==35 && c==1))
			{
				angle[b]= (angle[b] - ((a*5)/2))-5;
				c=0;
				b++;
				a=0;
			}
			
			x++;
		}
		if(x == 35)
		{
			printf("%d %d %d %d %d %d
                          %d %d %d %d %d %d
                          %d %d %d %d %d %d
                          %d %d %d %d %d %d
                          %d %d %d %d %d %d
                          %d %d %d %d %d\n\n",
			  distance[0],distance[1],distance[2],distance[3],distance[4],
              distance[5],distance[6],distance[7],distance[8],distance[9],
              distance[10],distance[11],distance[12],distance[13],
              distance[14],distance[15],distance[16],distance[17],
              distance[18],distance[19],distance[20],distance[21],
              distance[22],distance[23],distance[24],distance[25],
              distance[26],distance[27],distance[28],distance[29],
              distance[30],distance[31],distance[32],distance[33],
              distance[34]);
			if(angle[b]>179 || angle[b]<1)
			{
				b=b-1;
			}
			while(b>0)
			{
				if(mag[b]<400 && angle[b] > 90 && angle[b] < 145)
				{
					quadrant = 1;
				}
				if(mag[b]<400 && angle[b] > 45 && angle[b] < 90)
				{
					quadrant = 2;
				}
				if(mag[b]>400 && angle[b] > 0 && angle[b] < 90)
				{
					quadrant = 3;
				}
				if(mag[b]>400 && angle[b] > 90 && angle[b] < 180)
				{
					quadrant = 4;
				}
				printf("Object number %d magnitude = %d \r angle = %d and lies in quadrant %d\n\n", b, mag[b],angle[b], quadrant);
				b=b-1;
			}			
			x++;
		}
		pause(15000);
		PDC1=750;
		pause(2000);
		x=1;
		b=1;
		mag[1]=mag[2]=mag[3]=mag[4]=0;
		angle[1]=angle[2]=angle[3]=angle[4]=0;
	}

    return 0;
}

void forward()
{
	PDC1 = PDC1 + 10;
	pause(.5);
}
void back()
{
	PDC1 = PDC1 - 10;
	pause(.5);
}
void forwardnum(unsigned int pdc)
{
	PDC1 = PDC1 + pdc;
}
void backnum(unsigned int pdc2)
{
	PDC1 = PDC1 - pdc2;
	pause(10);
}

void setup()
{
    // Setup UART
    U1BRG = 48;            // 38400 baud @ 30 MIPS
    U1MODEbits.UARTEN = 1; // Enable UART

    // Configure D ports as a digital outputs
    LATD = 0;
    TRISD = 0b11110000;

    // Configure analog inputs
    TRISB = 0x01FF;      /* Port B all inputs */
    ADPCFG = 0xFF00;     /* PORTB 0-7 are analog inputs */
    ADCON1 = 0;          /* Manually clear SAMP to end sampling, start conversion*/
    ADCON2 = 0;          /* Voltage reference from AVDD and AVSS */
    ADCON3 = 0x0005;     /* Manual Sample, ADCS=5 -> Tad = 3*Tcy = 0.1us */
    ADCON1bits.ADON = 1; /* Turn ADC ON */

    // Configure PWM
    // PWM period = Tcy * prescale * PTPER = 0.33ns * 64 * 9470 = 20ms
    PWMCON1 = 0x00FF; // Enable all PWM pairs in complementary mode
    PTCON = 0;
    _PTCKPS = 3;      // prescale=1:64 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
    PTPER = 9470;     // 20ms PWM period (15-bit period value)
    PDC1 = 0;         // 0% duty cycle on channel 1 (max is 65536)
    PDC2 = 0;         // 0% duty cycle on channel 2 (max is 65536)
    PDC3 = 0;         // 0% duty cycle on channel 3 (max is 65536)
    PTMR = 0;         // Clear 15-bit PWM timer counter
    _PTEN = 1;        // Enable PWM time base
}
// Analogue input reading
unsigned int read_analog_channel(int channel)
{
    ADCHS = channel;          // Select the requested channel
    ADCON1bits.SAMP = 1;      // start sampling
    __delay32(30);            // 1us delay @ 30 MIPS
    ADCON1bits.SAMP = 0;      // start Converting
    while (!ADCON1bits.DONE); // Should take 12 * Tad = 1.2us
    return ADCBUF0;
}
void pause(unsigned long ms)
{
	__delay32(ms*30000L);
}

Example UART output of the program on the PICkit2 uart tool

Robotics

**Greetings! It’s been a while since I tended my blog, I’ve been busy running around college and whatnot. But it looks like that’s about to change since I’ll be using this blog to document my Robotics module, so let’s get started!**

Robotics 3.1

As was mentioned I am starting a new module called  robotics this year and I shall be documenting it using this blog.  On this first official post I shall be talking about what the module aims to teach us and the components given to us to work with. And some of my ideas that I’ll use the sensors for.

The module revolves around using the dsPIC30f4011 and integrating sensors and actuators to it. The dsPIC is the beefier brother of the normal PIC we used last year in the robosumo tournament, it has more PWM pins and more analog pins and can be seen in action on my last post. Another aspect of the module is communication with the dsPIC, the main mode we shall be using is UART as far as I know, but I’ve been reading about I2C for about a month now since I’ll be using this(hopefully) for my main event. Another big thing is the concept of Pulse Width Modulation which I talked about(in babytalk) again, in my last post. I’ll be explaining it a bit more thoroughly come the post where I use my servo.

On the second week of the module, we were given a ziploc bag containing sensors and actuators to work with. These materials are:

Materials on ziploc bag:

  1. dsPIC30f4011
  2. Servo Motor
  3. Stepper motor
  4. Geared DC motor
  5. H-Bridge
  6. 1 x OPB704 IR sensor
  7. Random small components

My own materials that I plan to use:

  1. Extra dsPIC & PIC
  2. PICkit2
  3. Sharp Rangefinder
  4. Extra IR sensors
  5. IR LEDs
  6. IR reciever
  7. Piezo-Electric transducer discs
  8. Resistance Temperature Detectors
  9. Light Dependent Resistors
  10. Wii nunchuck, Wiimote , and Wii Motion Plus

As it can be seen: most of the stuff given to us; we already worked with. The only new thing really is the PIC itself but only by a trivial factor. That’s all I can think of for now anyway. I already have templates for the Servo motor made a week after my DC motor post but there’s no point posting it now as it’s just a potentiometer controlling the angle, and that’s no fun! Coming up in a few hours : Stepper motor used to make an analog voltmeter. Sneak preview here.

Goodbye PIC, Hello dsPIC!

If I had a penny everytime I see “hello world!” I’d be one penny richer once more. I have been contemplating since the start of summer to create a new blog. But alas, as always, ended up procrastinating until now. It has been a few months since DIT’s ROBOSUMO tournament took place (in which yours truly’s team, participated in and won 2nd place or first loser, whichever you prefer. More of that here) and I have been itching for some time now to delve back into the world of microcontrollers. I originally planned to prepare for one of my upcoming modules; which is robotics, before the semester even started. This is the reason I bought my own materials i.e. PICkit2(which I shall be using all the time for the meantime), a dsPIC microcontroller, an H bridge to control DC motors, different types of sensor i.e. colour sensors and piezoelectric sensors, and many others. But enough about my boring ramblings, onto the show! PS: I shall try to refrain from unnecessary puns or jokes but odd ones might slip and so, I apologize in advance.

First task : DC motor control using Pulse Width Modulation

Materials used:

  • PICkit2 programmer
  • dsPIC30f4011
  • Breadboard and wires
  • Power Supply
  • 2 x DC motors
  • Soldering iron
  • LEDs
  • Various resistors
  • Microswitch
  • Trimmers used as potentiometers

I decided to go and give the dsPIC a test run since this is the chip I shall be using next year. To be honest I still don’t fully understand the difference between a normal PIC and a dsPIC but I have a rough idea, just looking at the pinouts on the datasheets it is clear that the dsPIC has more analogue inputs and way more PWM outputs than the last chip I used for C.A.K.E. which was the PIC16f4620. This will be for my advantage on this task as I will be needing two PWM outputs and three(for now) analogue inputs. Below is a block diagram of the task(click image to enlarge):

So in short : I’m making a sort of independent dimmer switches for two motors. I also forgot to add on the block diagram that I will be putting a microswitch to change the directions the motors are going(I intended for this to be a toggle switch but only a microswitch was available at the time but it does the job with a little software tweaking). So I twist the left pot: the left motor powers up accordingly, I twist the right pot: the right motor powers up, and I press the microswitch: the direction changes.

Pulse Width Modulation

I can’t go on without talking about PWM or Pulse Width modulation. This is a method to control power supplied to a point. By controlling the duty cycle one controls the amount of voltage coming out of a pin of a microcontroller. An analogy I told one of my friends once whilst trying to attempt to explain PWM was: imagine there was a conveyor belt of food and at the end is a fat man that consumes everything in sight, now the conveyor belt is designed to drop 10 cupcakes per second at the mouth of the fat man. Now let’s say that we want to make the fat man a bit thinner by putting him on a diet, that we’ll only give him half of his usual cupcakes i.e. 50% duty cycle, a way of doing this would be to turn off the conveyor belt every 5 seconds for the duration of 5 seconds. by doing this we limit the cupcakes to 50 per 10 seconds instead of the usual 100, the fat man still gets his cupcakes; only not as much so in theory he’d be less fat. This is exactly the same with PWM on the PIC only that instead of having a period of 10 seconds(5 on and 5 off) it will have a much smaller cycle time i.e. it will be doing this a lot lot faster.

Circuit

I haven’t used Inkscape in a good while so apologies if the image is a bit cluttered. As it can be seen on the schematic: the connections of the dsPIC are similar to the connections on a normal PIC, the main differences being the jumper wires connections the AVdd,AVss,Vdd, and Vss pins together, and of course the location of the TX and RX pins. Two trimmers connected up as potentiometers will be used to give a varied voltage signal to the analogue inputs of the dsPIC and this signal will determine the duty cycle that the dsPIC will give the motors. The microswitch is just connected to AN2 for the purpose of changing the directions, I will go into a bit more detail about this later on the program part of the post. The H bridge is connected to a different power supply as to prevent power fluctuations that might reset the pic or a worse case scenario: you draining too much power from the USB port of your computer via the PIC and as a safety feature; windows usually disables the USB port and you end up wasting time trying to enable it again, although there are ways to disable this safety feature, but I’m not here to talk about that. Anyways the H bridge is connected up as such, this is the same H bridge model as the one used on our ROBOSUMO project last year so I am very familiar with it. LEDs are connected both ways as to know which direction the motors are going, I used two sets: two red for backwards, and two green ones for forward(EDIT: The LEDs are actually connected in parallel with pin 2 and 7, and pin 10 and 15 and not across the motors although this might work as well).

The code

The programming environment that I’m going to be using is the MPLABX which is the new version of MPLAB, a C16 compiler is also needed to be able to program this particular MCU. I would like to acknowledge Mr. Ted Burke(www.batchloaf.wordpress.com) as the template I used to write this code are snippets of full codes found on his website(above), the part in particular that would have taken me days if not weeks to write myself would be the “configure pins” part of the program. The ADC part is familiar as I used it last year but of course I learned that off him, too! So go pay his blog a visit. I just edited some of the “setup” he has to suit my own needs.


//
// This program shows how PWM can be used to control two DC motors
// using a dsPIC and a motor driver chip.
// Two potentiometers control two motors independently
// Microswitch changes directions
//
// Setup template written by Ted Burke(batchloaf.wordpress.com)
// Program written by Gian Lorenzo
// Last edited 31/08/2012
//

#include <libpic30.h>
#include <p30f4011.h>
#include <stdio.h>
#include <xc.h>

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16);  // Fosc=16x7.5MHz, Fcy=30MHz
_FWDT(WDT_OFF);                   // Watchdog timer off
_FBORPOR(MCLR_DIS);               // Disable reset pin

// Function prototypes
void setup();
unsigned int read_analog_channel(int n);

int main()
{
    int voltage,voltage2,direction,flag;
    double tmin = 0.0005;
    double tmax = 0.02;

    // Set up which pins are which and activate UART tool
    setup();
    direction = 1;
    while(1)
    {
        voltage = read_analog_channel(0);
        PDC1 = 937509.375 * (tmin + (voltage / 1023.0) * (tmax - tmin));
        voltage2 = read_analog_channel(1);
        PDC2 = 937509.375 * (tmin + (voltage2 / 1023.0) * (tmax - tmin));
        if(direction == 1)
        {
            _LATD0=1;
            _LATD2=0;
            _LATD1=1;
            _LATD3=0;
            __delay32(1000000);
            if(read_analog_channel(2) < 500)
            {
                flag = 1;
            }
            if(flag == 1 && read_analog_channel(2) >500)
            {
                direction = 1-direction;
                flag = 0;
            }
        }
        if(direction == 0)
        {
            _LATD0=0;
            _LATD2=1;
            _LATD1=0;
            _LATD3=1;
            if(read_analog_channel(2) < 500)
            {
                flag = 1;
            }
            if(flag == 1 && read_analog_channel(2) >500)
            {
                direction = 1-direction;
                flag = 0;
            }
        }
    }
    return 0;
}

void setup()
{
    // Setup UART
    U1BRG = 48;            // 38400 baud @ 30 MIPS
    U1MODEbits.UARTEN = 1; // Enable UART

    // Configure D ports as a digital outputs
    LATD = 0;
    TRISD = 0b11110000;

    // Configure analog inputs
    TRISB = 0x01FF;      /* Port B all inputs */
    ADPCFG = 0xFF00;     /* PORTB 0-7 are analog inputs */
    ADCON1 = 0;          /* Manually clear SAMP to end sampling, start conversion*/
    ADCON2 = 0;          /* Voltage reference from AVDD and AVSS */
    ADCON3 = 0x0005;     /* Manual Sample, ADCS=5 -> Tad = 3*Tcy = 0.1us */
    ADCON1bits.ADON = 1; /* Turn ADC ON */

    // Configure PWM
    // PWM period = Tcy * prescale * PTPER = 0.33ns * 64 * 9470 = 20ms
    PWMCON1 = 0x00FF; // Enable all PWM pairs in complementary mode
    PTCON = 0;
    _PTCKPS = 3;      // prescale=1:64 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
    PTPER = 9470;     // 20ms PWM period (15-bit period value)
    PDC1 = 0;         // 0% duty cycle on channel 1 (max is 65536)
    PDC2 = 0;         // 0% duty cycle on channel 2 (max is 65536)
    PDC3 = 0;         // 0% duty cycle on channel 3 (max is 65536)
    PTMR = 0;         // Clear 15-bit PWM timer counter
    _PTEN = 1;        // Enable PWM time base
}

// Analogue input reading
unsigned int read_analog_channel(int channel)
{
    ADCHS = channel;          // Select the requested channel
    ADCON1bits.SAMP = 1;      // start sampling
    __delay32(30);            // 1us delay @ 30 MIPS
    ADCON1bits.SAMP = 0;      // start Converting
    while (!ADCON1bits.DONE); // Should take 12 * Tad = 1.2us
    return ADCBUF0;
}

As I said earlier I had to make a few tweaks to make the microswitch into a toggle one thus the code is a bit longer, even so this is still a real basic task. In the end I got it to work, but talk is cheap so:

If I was any good at fabrication I’d try to make this into a sort of toy crane like contraption, the motors would of course control the horizontal and vertical movements of the arm. Or whenever I figure out how to send commands via UART I can make a toy remote control car, whichever comes first! For the next task I plan on using a sharp rangefinder(another surplus from the ROBOSUMO project, the DC motors and the microswitches are also surplus parts of C.A.K.E.), some colour sensors, and maybe even the piezo electric sensor. Here’s to hoping inspiration for a task breezes towards my direction soon.