Quantcast
Channel: ad hocumentation • n. fast documentation of ideas and solutions.
Viewing all 72 articles
Browse latest View live

First attempt at printing to an LCD display from a dsPIC30F4011

$
0
0

The following example is my first attempt at printing to an LCD display using the dsPIC30F4011 microcontroller. The display I’m using is a 16×1 character screen with what I think is a standard Hitachi interface. It’s my first time using one of these, so the code is a bit long-winded at the moment, but the good news is that it’s working!

Photo of dsPIC driving LCD display

This LCD module uses the Hitachi interface, which has 4-bit and 8-bit operating modes. Here, the 4-bit mode is used, which means that each byte transmitted from the dsPIC to the LCD module is split into two 4-bit nibbles which are transmitted one after the other. The advantage of 4-bit mode is that less microcontroller pins are required – 7 digital outputs in total: 3 control lines and 4 data lines. (Actually, strictly speaking, you could get away with only 6 lines.)

The circuit diagram is shown below. Note that the 5 connections to the PICkit 2 USB programmer are not shown. (Link to editable SVG version of this image, created using Inkscape.)

Circuit diagram of dsPIC30F4011 connected to Hitachi LCD displlay module

The 7 connections between the dsPIC’s digital outputs and the LCD module are:

LCD pin number LCD pin name LCD pin description dsPIC pin number dsPIC pin name
4 RS 0: command transfer
1: data transfer
16 RC14
5 R/W 0: write data
1: read data
(always 0 here)
15 RC13
6 E D4-D7 latched on
falling edge of E
14 RC15
11 D4 Bit 0 (lsb) 2 RB0
12 D5 Bit 1 3 RB1
13 D6 Bit 2 4 RB2
14 D7 Bit 3 (msb) 5 RB3

The LCD module I’m using is shown below (front and back views of the same device). The 6-pin header at one end of the breadboard is the connector for the PICkit 2 USB programmer.

Front and back views of the LCD display module

This is the complete breadboard circuit including the dsPIC and LCD module (two views of the same circuit):

Two views of the complete dsPIC / LCD display system

I was surprised to discover that 16×1 LCD modules of this type (1 line with 16 characters of text) are typically structured as if they had 2 lines of 8 characters. The first 8 characters are “line 1″ and the second 8 characters are “line 2″. As a result, in the program below, half of the single line of text to be displayed is written to “line 1″ and half is written to “line 2″.

This is the C code for the dsPIC program:

//
// LCD display program for dsPIC30F4011
// Written by Ted Burke - Last updated 16-4-2013
//

#include <xc.h>
#include <libpic30.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

#define RS_PIN _LATC14
#define RW_PIN _LATC13
#define E_PIN _LATC15

void delay_ms(unsigned int n);
void send_nibble(unsigned char nibble);
void send_command_byte(unsigned char byte);
void send_data_byte(unsigned char byte);

int main()
{
	_TRISD0 = 0; // Make RD0 an output
	
	TRISC = 0; // RC13-15 as digital outputs
	TRISB = 0xFFF0; // RB0-3 as digital outputs
	_PCFG0 = 1; // AN0 is digital
	_PCFG1 = 1; // AN1 is digital
	_PCFG2 = 1; // AN2 is digital
	_PCFG3 = 1; // AN3 is digital

	// Let's just write to the LCD and never read!
	// We'll wait 2ms after every command since we can't
	// check the busy flag.
	RW_PIN = 0;
	RS_PIN = 0;
	E_PIN = 1;
	
	// Initialisation
	delay_ms(16); // must be more than 15ms
	send_nibble(0b0011);
	delay_ms(5); // must be more than 4.1ms
	send_nibble(0b0011);
	delay_ms(1); // must be more than 100us
	send_nibble(0b0011);
	delay_ms(5); // must be more than 4.1ms
	send_nibble(0b0010); // select 4-bit mode
	
	// Display settings
	send_command_byte(0b00101000); // N=0 : 2 lines (half lines!), F=0 : 5x7 font
	send_command_byte(0b00001000); // Display: display off, cursor off, blink off
	send_command_byte(0b00000001); // Clear display
	send_command_byte(0b00000110); // Set entry mode: ID=1, S=0
	send_command_byte(0b00001111); // Display: display on, cursor on, blink on
	
	// Define two 8 character strings
	const char line1[] = " Ted's d";
	const char line2[] = "sPIC30F ";
	
	// Write the two strings to lines 1 and 2
	int n;
	send_command_byte(0x02); // Go to start of line 1
	for (n=0 ; n<8 ; ++n) send_data_byte(line1[n]);
	send_command_byte(0xC0); // Go to start of line 2
	for (n=0 ; n<8 ; ++n) send_data_byte(line2[n]);
	
	// Now just blink LED indefinitely
	while(1)
	{
		_LATD0 = 1 - _LATD0;
		delay_ms(500);
	}
}

// Delay by specified number of milliseconds
void delay_ms(unsigned int n)
{
	while(n--) __delay32(30000);
}

void send_nibble(unsigned char nibble)
{
	// Note: data is latched on falling edge of pin E
	LATB = nibble;
	delay_ms(1);
	E_PIN = 0;
	delay_ms(1);
	E_PIN = 1;
	delay_ms(2); // Enough time even for slowest command
}

// Send a command byte (i.e. with pin RS low)
void send_command_byte(unsigned char byte)
{
	RS_PIN = 0;
	send_nibble(byte >> 4);
	send_nibble(byte & 0xF);
}

// Send a data byte (i.e. with pin RS high)
void send_data_byte(unsigned char byte)
{
	RS_PIN = 1;
	send_nibble(byte >> 4);
	send_nibble(byte & 0xF);
}

I compiled this with Microchip’s XC16 compiler, using the following simple build script.

xc16-gcc main.c -mcpu=30F4011 -Wl,--script=p30F4011.gld
if errorlevel 0 xc16-bin2hex a.out

To use the build script:

  1. Create a new folder.
  2. Save the C program in that folder as “main.c”.
  3. Save the build script in the same folder as “build.bat”.
  4. Open a console window and navigate to that folder.
  5. Type “build.bat” to compile the program.
  6. Use the PICkit 2 application (or another program) to download the compiled program (file “a.hex”) onto the dsPIC.

References

  1. Wikipedia article on Hitachi HD44780 LCD controller (contains useful summary of connections and commands)
  2. Book: PIC Microcontrollers, Appendix B: Examples (scroll down to the example titled “LCD DISPLAY”)
  3. HD44780 LCD controller datasheet, containing all the gory details (courtesy of Sparkfun who sell lots of LCD modules)


PWM servo control example for the PIC18F14K50

$
0
0

This is my first program for the PIC18F14K50 20-pin microcontroller. I haven’t used this chip before, but it seems to have some useful features including USB connectivity, so I intend to investigate it further.

This program is a simple PWM example which moves a servo motor to one of two possible angles (0 degrees or 90 degrees) depending on the state of a digital input pin (RC4, pin 6). The PWM output signal is produced on pin 5 (CCP1).

I’m using the internal RC oscillator. This has a default Fosc of 1MHz, but in order to achieve a PWM period of 20ms (for the servo), I had to slow the clock down to 500kHz, which gives an instruction cycle of 8us.

//
// PIC18F14K50 servo PWM example program
// Written by Ted Burke (http://batchloaf.com)
// Last updated 22-4-2013
//
// To compile with XC8:
//     xc8 --chip=18F14K50 main.c
//
 
#include <xc.h>
 
#pragma config FOSC=IRC,MCLRE=OFF,WDTEN=0,LVP=OFF,BOREN=OFF
 
int main(void)
{
	// Set clock frequency to 500kHz, therefore Tcy = 8us
	OSCCONbits.IRCF = 0b010;
	
	// Set up PWM
	CCP1CON = 0b00001100;   // Enable PWM on CCP1
	TRISC = 0b11011111;		// Make CCP1 an output
	T2CON = 0b00000110;     // Enable TMR2 with prescaler = 16
	PR2 = 155;	// PWM period = (PR2+1) * prescaler * Tcy = 19.968ms
	CCPR1L = 8;	// pulse width = CCPR1L * prescaler * Tcy = 1.024ms
	
	while(1)
	{
		if (PORTCbits.RC4) CCPR1L = 12;	// 1.536ms pulses, i.e. 90 degrees
		else CCPR1L = 8;				// 1.024ms pulses, i.e. 0 degrees
	}
	
	return 0;
}

I don’t have an actual PIC18F14K50 to try my program on, so instead I ran it on the simulator in MPLAB v8.50. I captured the simulated PWM output using MPLAB’s logic analyzer tool, as shown below.

simulator_PWM_logic_analyzer

The blue waveform is the PWM output on CCP1 (pin 5). The red vertical lines are “cursors” provided in the logic analyzer for measuring the time difference between two points on the waveform. I positioned them to measure the period of the PWM waveform, which is 2511 instruction cycles. Since the clock oscillator frequency Fosc is set to 500kHz, the clock cycle Tosc is 2us. The PIC18F14K50 performs one machine instruction every 4 clock cycles, so the instruction cycle Tcy is 8us. So the PWM period is:

T_{pwm} = 2511 \times T_{cy} = 20.088ms

Starting with Circuitry

$
0
0

Reblogged from RoboSlam:

Click to visit the original post
  • Click to visit the original post

RoboSlam starts with an introduction to the overall robot-building process and then delves into assembling the electronic components (i.e., building the circuitry).  The photos below track the progress of several groups of participants in the "Engineering Your Future" event held the week of May 13-17 at DIT.

These photos were all taken by DIT's current Fulbright Scholar in Engineering Education, Dr.

Read more… 31 more words

We ran a great RoboSlam robot-building workshop in DIT Kevin St last Tuesday (followed by some related events in DIT Bolton St later in the week). Shannon Chance, who is the research PI on the RoboSlam project, got loads of great photos and has started posting about it on the RoboSlam blog and her own blog.

50Hz square wave example for PIC16F1829

$
0
0

This post presents two example programs for the PIC16F1829 microcontroller that generate 50Hz square waves. The first example uses simple bit banging to generate the waveform. The second example uses the dedicated PWM hardware module.

Example 1: bit banging

In this example program, I set a single pin as a digital output (RC5), then generate a 50Hz square wave using straightforward bit banging.

The clock oscillator is left at its default frequency of Fosc = 500kHz. Therefore, the oscillator period Tosc = 2us. Since the PIC16F1829 performs one machine instruction every four oscillator cycles, the instruction cycle, Tcy = 8us.

//
// PIC16F1829 50Hz 50% duty cycle example program
// Written by Ted Burke (http://batchloaf.com)
// Last updated 5-6-2013
//
// To compile with XC8:
//     xc8 --chip=16F1829 main.c
//
  
#include <xc.h>

// Select the internal oscillator (default Fosc = 500kHz, which gives
// an instruction cycle Tcy = 8us), disable low voltage programming,
// disable reset pin, disable watchdog timer
#pragma config FOSC=INTOSC,LVP=OFF,MCLRE=OFF,WDTE=OFF

int main(void)
{
	// Make RC5 a digital output
	TRISCbits.TRISC5 = 0;

	// Now just switch RC5 high and low repeatedly
	while(1)
	{
		LATCbits.LATC5 = 1; // Set RC5 high
		_delay(1250); // 10ms delay @ Fosc = 500kHz, Tcy = 8us
		LATCbits.LATC5 = 0; // Set RC5 low
		_delay(1250); // 10ms delay @ Fosc = 500kHz, Tcy = 8us
	}

    return 0;
}

I don’t have a PIC16F1829 to try this out on, so the best I could do was test it using MPLAB SIM (the simulator in MPLAB v8.50). Here’s a screenshot I captured of the Simulator Logic Analyzer tool (under the View menu in MPLAB) displaying the generated waveform.

MPLAB Simulator Logic Analyzer screenshot showing generated square wave

The x-axis units in the figure above are instruction cycles, so the period of the generated signal is approximately 2500 instruction cycles.

\textrm{frequency} = \frac{1}{2500 \times 8 \times 10^{-6}} = 50 \textrm{Hz}

Careful examination of the waveform in the figure above reveals that the period is actually not precisely 2500 instruction cycles. This may be due to the overhead associated with the additional instructions caused by the while loop and bit-banging. If precision is required, the delay values could be carefully tweaked to compensate. However, it is worth bearing in mind that the frequency of the internal RC oscillator can vary, so if precision is required an external crystal oscillator might be a better choice.

Example 2: using the PWM module

Here’s another example of a 50Hz square wave. This time, the PWM module is used:

//
// PIC16F1829 50Hz 50% duty cycle example program
// Written by Ted Burke (http://batchloaf.com)
// Last updated 7-6-2013
//
// To compile with XC8:
//     xc8 --chip=16F1829 main.c
//
  
#include <xc.h>

// Select the internal oscillator (default Fosc = 500kHz, which gives
// an instruction cycle Tcy = 8us), disable low voltage programming,
// disable reset pin, disable watchdog timer
#pragma config FOSC=INTOSC,LVP=OFF,MCLRE=OFF,WDTE=OFF

int main(void)
{
	// Set up PWM
	T2CON = 0b00000111;   // Enable TMR2 with prescaler = 64
	PR2 = 155;            // PWM period = (PR2+1) * 64 * Tcy = 19.968ms
	CCPR1L = 78;          // pulse width = CCPR1L * 64 * Tcy = 9.984ms
	CCP1CON = 0b00001100; // Enable PWM on CCP1
	TRISCbits.TRISC5 = 0; // Make CCP1 pin an output
	
	// Now just do nothing while PWM module does the work
	while(1);
	
	return 0;
}

Configuring the PWM module is a little more complicated than just bit banging to generate the signal. However, it has (at least) two very important benefits:

  1. The program can get on with other tasks while the square wave is generated continuously in the background by the dedicated PWM hardware module.
  2. Since there are no extra instructions associated with a while loop or anything else, it’s much easier to determine the exact length of the waveform period and pulse width (measured in instruction cycles).

Simple PI control using the dsPIC30F4011

$
0
0

This is an initial sketch of a simple PI controller using the dsPIC30F4011.

I hope to add in quite a bit more detail and explanation to accompany this in the coming days, but I thought I’d post the current code for the time being since someone is currently waiting to use it in a project.

What I’ve done so far is very closely modeled on this excellent example on Wikipedia. However, I removed the derivative term since I’m just creating a PI controller.

//
// main.c - PI control with a dsPIC30F4011
// Written by Ted Burke
// Last updated 11-6-2013
//
// This example program shows a simple way to perform PI control
// on a system such as a DC voltage converter. The controller
// output takes the form of two complementary PWM channels (with
// 2us dead time between them). The PWM outputs are OC1 (pin 23)
// and OC2 (pin 18). I'm currently just driving an RC low pass
// filter (R = 220 ohm, C = 1000 uF) with PWM from pin 23 and
// feeding the filtered voltage back to an analog input (AN1).
// The setpoint is determined by the voltage on another analog
// input (AN0). Bascially, the PI controller adjusts the PWM
// duty cycle to make AN1 match AN0. The PWM duty cycle is
// clamped to the range 0.2 to 0.8.
//
// The PWM period is 50us, so the PWM frequency is 20kHz.
// The dead time is set to 2us which is in the right ballpark
// for IGBT devices, assuming some kind of push-pull system.
// The coefficients Kp and Ki are currently just a complete
// guess - these should be tuned for the actual system.
// dt is the sample time, which is 50us since the controller
// calculation is repeated at the start of each PWM cycle.
//
 
#include <xc.h>
#include <libpic30.h>
 
// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, i.e. 30 MIPS
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin 

unsigned int read_analog_channel(int channel);

int main()
{
	// Define time values
	int Tpwm = 1500; // PWM period = 1500 * Tcy = 50us (i.e. 20kHz)
	int dead_time = 60; // 2us deadtime is in the right ballpark for IGBTs
	int Ton = Tpwm - dead_time - dead_time; // total on time during each cycle
	double duty_cycle = 0.5;

	double setpoint, measured_value, error, output;
	double previous_error = 0, integral = 0 ;
	double dt = 0.00005, Kp = 0.001, Ki = 0.001;
	
	// Make all port D pins outputs
	TRISD = 0;
	
	// Configure AN0-AN8 as analog inputs
	ADCON3bits.ADCS = 15;  // Tad = 266ns, conversion time is 12*Tad
	ADCON1bits.ADON = 1;   // Turn ADC ON
	
	// Configure timer 2 (default timer for output compare)
	PR2 = Tpwm;         // 20kHz PWM frequency
	T2CONbits.TON = 1;  // Enable timer 2
	
	// Initialise OC channel 1 & 2 start and stop times
	OC1R = 0;
	OC1RS = (int)(duty_cycle * Ton);
	OC2R = OC1RS + dead_time;
	OC2RS = PR2 - dead_time;
	
	// Set output compare mode for continuous pulses
	OC1CONbits.OCM = 0b101;
	OC2CONbits.OCM = 0b101;
	
	while(1)
	{
		while(_T2IF == 0); // wait for start of next cycle
		_T2IF = 0;
		
		setpoint = read_analog_channel(0);
		measured_value = read_analog_channel(1);
		
		error = setpoint - measured_value;
		integral = integral + error*dt;
		output = Kp*error + Ki*integral;
		previous_error = error;
		
		duty_cycle = output;
		if (duty_cycle > 0.8) duty_cycle = 0.8;
		if (duty_cycle < 0.2) duty_cycle = 0.2;
		
		OC1RS = (int)(duty_cycle * Ton);
		OC2R = OC1RS + dead_time;
	}
	
	return 0;
}

// This function reads a single sample from the specified
// analog input. It should take less than 5us when the
// microcontroller is running at 30 MIPS.
// The dsPIC30F4011 has a 10-bit ADC, so the value
// returned is between 0 and 1023 inclusive.
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 = 3.2us
	return ADCBUF0;
}

David Dorran’s PIC bootloader

$
0
0

David Dorran, one of my colleagues in the School of Electrical and Electronic Engineering here in the Dublin Institute of Technology, has created a wonderful bootloader for the PIC18F4620 microcontroller:

David Dorran’s PIC18F4620 Bootloader

A bootloader is basically a very small program which resides in the flash memory of a microcontroller alongside the main program which the developer has written to control the chip’s behaviour during normal operation. What normally happens is that the bootloader runs first whenever the chip is powered up. It then does one of two things – either

  1. The main program is simply executed as normal, or
  2. The bootloader begins downloading a new version of the main program from an external source (probably a PC) and overwriting the previous version in flash memory.

Different bootloaders use different criteria for choosing whether to enter normal operation or update the microcontroller’s software, but it’s usually something relatively simple like the state (high or low) of a specific pin at the moment the chip is powered up (which is exactly what Dave’s bootloader does).

What’s great about bootloaders is that they allow you to update the software on a microcontroller using a very simple and cheap hardware device such as a USB-to-serial converter, rather than using a dedicated programming device (such as the PICkit 2 for the PIC) which may be considerably more expensive.

In our school, we use a lot of PIC microcontrollers and they’re really great to work with. However, one downside of using them in teaching is that the USB programming interface is relatively expensive – around €30 for the PICkit 2. Dave’s bootloader will allow us (and others) to program each chip once with a PICkit 2 to put the bootloader on it, and then give it to a student with a €5 USB-to-serial converter, which they can use to program it from then on.

As well as the actual bootloader (that’s the tiny code that actually runs on the microcontroller each time it is powered up) Dave has provided a very clear circuit diagram and a build script for use with the XC8 compiler when you’re compiling your own code for the PIC.

I think this is really exciting news for teachers or anyone else using PIC microcontrollers on a tight budget – Well done Dave! Here’s that link again:

David Dorran’s PIC18F4620 Bootloader


Basic LCD display example for the PIC18F4620

$
0
0

I previously posted an LCD display example program for the dsPIC30F4011 microcontroller. This is the same program adapted for the PIC18F4620 microcontroller. I’ll have to update this later with a circuit diagram, but here’s a quick photo of the program running on a breadboard circuit. Hopefully you can work out which pins are which from the code and/or photo, as well as by referring to the more complete documentation for the dsPIC30F4011 example.

Photo of LCD display example program for PIC18F4620 running on a breadboard circuit

//
// lcd.c - LCD display examplefor dsPIC18F4620
// Written by Ted Burke - Last updated 8-10-2013
//

#include <xc.h>

// Select clock oscillator (default frequency Fosc=1MHz -> Tcy = 4us).
// Disable reset pin, watchdog timer, low voltage programming and
// brown-out reset.
#pragma config OSC=INTIO67,MCLRE=OFF,WDT=OFF,LVP=OFF,BOREN=OFF

// Select which pins the program will use for the LCD screen
// control signals, RS, RW and E.
// NB I had to change these from the names used in my previous
// dsPIC30F4011 example to avoid a clash with the equivalent
// definitions in the XC8 compiler's peripheral library.
#define PIN_RS LATDbits.LATD7
#define PIN_RW LATDbits.LATD6
#define PIN_E LATDbits.LATD5

// Select a pin to use for the flashing LED
#define PIN_LED LATDbits.LATD4

// Function prototypes for transmitting to LCD
void delay_ms(unsigned int n);
void send_nibble(unsigned char nibble);
void send_command_byte(unsigned char byte);
void send_data_byte(unsigned char byte);
 
int main()
{
    TRISD = 0b00000000; // Set RD0-7 as digital outputs
    
    // Let's just write to the LCD and never read!
    // We'll wait 2ms after every command since we can't
    // check the busy flag.
    PIN_RW = 0;
    PIN_RS = 0;
    PIN_E = 1;
     
    // Initialisation
    delay_ms(16); // must be more than 15ms
    send_nibble(0b0011);
    delay_ms(5); // must be more than 4.1ms
    send_nibble(0b0011);
    delay_ms(1); // must be more than 100us
    send_nibble(0b0011);
    delay_ms(5); // must be more than 4.1ms
    send_nibble(0b0010); // select 4-bit mode
    
    // Display settings
    send_command_byte(0b00101000); // N=0 : 2 lines (half lines!), F=0 : 5x7 font
    send_command_byte(0b00001000); // Display: display off, cursor off, blink off
    send_command_byte(0b00000001); // Clear display
    send_command_byte(0b00000110); // Set entry mode: ID=1, S=0
    send_command_byte(0b00001111); // Display: display on, cursor on, blink on
     
    // Define two 8 character strings
    const char line1[] = "  Ted's ";
    const char line2[] = "PIC18F  ";
     
    // Write the two strings to lines 1 and 2
    int n;
    send_command_byte(0x02); // Go to start of line 1
    for (n=0 ; n<8 ; ++n) send_data_byte(line1[n]);
    send_command_byte(0xC0); // Go to start of line 2
    for (n=0 ; n<8 ; ++n) send_data_byte(line2[n]);
     
    // Now just blink LED indefinitely
    while(1)
    {
        PIN_LED = 1;
        delay_ms(500);
        PIN_LED = 0;
        delay_ms(500);
    }
}
 
// Delay by specified number of milliseconds
void delay_ms(unsigned int n)
{
    // At Fosc=1Mhz, Tcy is 4us. That's the time
    // taken to perform one machine code instruction.
    // Therefore a delay of 250 x Tcy = 1ms.
    while(n--) _delay(250);
}
 
void send_nibble(unsigned char nibble)
{
    // Set RD0-3 without affecting RD4-7
    LATD = (LATD & 0xF0) + nibble;
    delay_ms(1);
    // Note: data is latched on falling edge of pin E
    PIN_E = 0;
    delay_ms(1);
    PIN_E = 1;
    delay_ms(2); // Enough time even for slowest command
}
 
// Send a command byte (i.e. with pin RS low)
void send_command_byte(unsigned char byte)
{
    PIN_RS = 0;
    send_nibble(byte >> 4);
    send_nibble(byte & 0xF);
}
 
// Send a data byte (i.e. with pin RS high)
void send_data_byte(unsigned char byte)
{
    PIN_RS = 1;
    send_nibble(byte >> 4);
    send_nibble(byte & 0xF);
}

I compiled the program using Microchip’s XC8 compiler. This was the command I used:

xc8 --chip=18F4620 lcd.c

I then used the PICkit 2 application to transfer the compiled hex file onto the microcontroller.


Relaxation Oscillator


Videos from Robotics 3.1

$
0
0

I teach a Robotics module in the final year of our DT009 Electrical Engineering programme here in DIT Kevin St. The philosophy of the module is to give the students enough bits and pieces to get stuck into designing and building some small-scale robotic systems that they dream up themselves. It’s a very enjoyable module to teach (and hopefully to participate in!) because it spans a really useful range of problem solving technology and theory (sensors, actuators, programming, interfacing, kinematics, etc) and it involves many hours of enjoyable robot hacking in the lab.

One of the things I like most about this module is that every year people come up with brilliant ideas of things to build. Everyone on the module (including me) documents their work on WordPress blogs, which means that a lot of the useful stuff learned during the design and implementation process is available immediately for others to learn from. Each year, it takes a little while to build up momentum but after a few weeks, little gems start appearing on the students’ blogs. I’m just beginning to see some interesting stuff appearing on this year’s blogs, so I thought I’d share a few videos that caught my eye over the last few days. All three of these use a $2 stepper motor driven by a dsPIC30F4011 microcontroller, via a SN754410NE driver chip. These three components are included in the kit I give out at the start of the module.

This video by Simon Kirwan is of his Countdown Clock, complete with the music from the iconic TV show. You can read more about this on Simon’s blog.

This video by Jonathan Baldwin is of a mock-up of a light-tracking solar cell. There’s no actual PV cell (yet?) but it really does track the light. An array of four light-dependent resistors is used to work out the direction of brightest light and then a stepper motor is used to face the dummy solar panel in that direction. You can read more about how it works on Jonathan’s blog.

Finally, this stone cold classic from Jason Franey almost eclipses the original Kylie video. Ok, maybe not quite.

You can read more on Jason’s blog.

Edit: Two more great videos hot off the presses! Both are servo-based systems.

The first is an object tracker by Aron Horan. A SHARP infrared rangefinder is attached to a low-cost servo and it scans back and forth looking for an object. You can read more on Aron’s blog.

The second is a servo pointer controlled by an ultrasonic rangefinder. This one was created by Andrea McConnon – you can read about it in detail on her blog.


Simple ARM example for LPC1114

$
0
0

My Colleagues Frank Duignan and Richard Hayes have been experimenting with an ARM microcontroller in a dual inline package (DIP) which can be plugged straight into a breadboard. The chip they’re using is LPC1114FN28/102, which is made by NXP and available for less than €2. Frank spent a bit of time working out how to program the chip using a low-cost USB-to-serial converter, so the whole thing provides a micrcontroller programming setup that’s incredibly inexpensive, but quite powerful!

Under Frank’s guidance, I’ve just implemented a simple flashing LED example for the ARM LPC1114. The following code is basically a stripped back version of the more complex example he sent me.

//
// main.c - Blinking LED example for ARM LPC1114
// Written by Ted Burke (based on code by Frank Duignan)
// Last updated 29-11-2013
//

#include "lpc111x.h"

int main()
{
    // Turn on clock for GPIO, IOCON and ADC
    SYSAHBCLKCTRL |= BIT6 + BIT13 + BIT16;
    GPIO0DIR = BIT8; // Make PIO0_8 an output
    GPIO0DATA = 0;   // Turn off PIO0 outputs
    
    int n;
    while(1)
    {
        GPIO0DATA = BIT8; // Turn on PIO0_8
        n=1000000; while(--n);
        GPIO0DATA = 0;    // Turn on PIO0_8
        n=1000000; while(--n);
    }    
}

I’m using the following build script (“build.bat”), courtesy of Frank Duignan.

arm-none-eabi-gcc -mcpu=cortex-m0 -mthumb -g -c init.c -o init.o
arm-none-eabi-gcc -mcpu=cortex-m0 -mthumb -g -c main.c -o main.o
arm-none-eabi-ld init.o main.o -L "c:\Program Files\GNU Tools ARM Embedded\4.7 2013q3\lib\gcc\arm-none-eabi\4.7.4\armv6-m" -lgcc -T linker_script.ld --cref -Map main.map -nostartfiles -o main.elf
"C:\Program Files\GNU Tools ARM Embedded\4.7 2013q3\arm-none-eabi\bin\objcopy" -O ihex main.elf main.hex

I used the following ARM version of gcc to build the program:

https://launchpad.net/gcc-arm-embedded/4.7/4.7-2013-q3-update/+download/gcc-arm-none-eabi-4_7-2013q3-20130916-win32.exe

I used Flash Magic to transfer the compiled code (main.hex) to the microcontroller:

http://www.flashmagictool.com/download.html&d=FlashMagic.exe

There are a couple of other bits and pieces that are required to build the program, namely a header file “lpc111x.h”, a linker script “linker_script.ld” and a C file with some init code “init.c”. Frank is planning to post those online over the weekend, so I’ll update this post with links once I have them.


Simple DFT in C

$
0
0

I’ve been spending some time recently thinking about the Discrete Fourier Transform (DFT), as well as the Fast Fourier Transform (FFT) which provides a computationally efficient means of calculating the DFT of a signal.

The DFT is defined as follows. Given a discrete-time sequence x[n] where n = 0,1,2,….,N-1

X[k] = \sum\limits_{n=0}^{N-1} x[n] e^{-\frac{jkn2\pi}{N}}

De Moivre’s Theorem tells us that

e^{j\theta} = cos(\theta) + jsin(\theta)

Therefore, we can rewrite the DFT summation as separate expressions for the real and imaginary part of X[k] as follows (assuming x[n] is real).

X[k] = X_{re}[k] + jX_{im}[k]

where

X_{re}[k] = \sum\limits_{n=0}^{N-1} x[n].cos(\frac{jkn2\pi}{N})

and

X_{im}[k] = -\sum\limits_{n=0}^{N-1} x[n].sin(\frac{jkn2\pi}{N})

As part of my thought process, I’ve been sketching out different implementations of the DFT and FFT in C and MATLAB/Octave. This one struck me as particularly nice because it’s a very plain and simple DFT by brute force.

//
// dft.c - Simple brute force DFT
// Written by Ted Burke
// Last updated 7-12-2013
//
// To compile:
//    gcc dft.c -o dft.exe
// 
// To run:
//    dft.exe
//

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define N 16
#define PI2 6.2832

int main()
{
	// time and frequency domain data arrays
    int n, k;             // indices for time and frequency domains
    float x[N];           // discrete-time signal, x
    float Xre[N], Xim[N]; // DFT of x (real and imaginary parts)
    float P[N];           // power spectrum of x
    
    // Generate random discrete-time signal x in range (-1,+1)
    srand(time(0));
    for (n=0 ; n<N ; ++n) x[n] = ((2.0 * rand()) / RAND_MAX) - 1.0;
    
    // Calculate DFT of x using brute force
    for (k=0 ; k<N ; ++k)
    {
        // Real part of X[k]
        Xre[k] = 0;
        for (n=0 ; n<N ; ++n) Xre[k] += x[n] * cos(n * k * PI2 / N);
        
        // Imaginary part of X[k]
        Xim[k] = 0;
        for (n=0 ; n<N ; ++n) Xim[k] -= x[n] * sin(n * k * PI2 / N);
        
        // Power at kth frequency bin
        P[k] = Xre[k]*Xre[k] + Xim[k]*Xim[k];
    }
    
    // Output results to MATLAB / Octave M-file for plotting
    FILE *f = fopen("dftplots.m", "w");
    fprintf(f, "n = [0:%d];\n", N-1);
    fprintf(f, "x = [ ");
    for (n=0 ; n<N ; ++n) fprintf(f, "%f ", x[n]);
    fprintf(f, "];\n");
    fprintf(f, "Xre = [ ");
    for (k=0 ; k<N ; ++k) fprintf(f, "%f ", Xre[k]);
    fprintf(f, "];\n");
    fprintf(f, "Xim = [ ");
    for (k=0 ; k<N ; ++k) fprintf(f, "%f ", Xim[k]);
    fprintf(f, "];\n");
    fprintf(f, "P = [ ");
    for (k=0 ; k<N ; ++k) fprintf(f, "%f ", P[k]);
    fprintf(f, "];\n");
    fprintf(f, "subplot(3,1,1)\nplot(n,x)\n");
    fprintf(f, "xlim([0 %d])\n", N-1);
    fprintf(f, "subplot(3,1,2)\nplot(n,Xre,n,Xim)\n");
    fprintf(f, "xlim([0 %d])\n", N-1);
    fprintf(f, "subplot(3,1,3)\nstem(n,P)\n");
    fprintf(f, "xlim([0 %d])\n", N-1);
    fclose(f);
    
	// exit normally
    return 0;
}

To compile and run the above code, do the following:

Compiling and running dft.c in the console

When you run the executable file dft.exe, it writes an M-file called dftplots.m which can then be run in MATLAB or Octave to produce plots of the results. This is the M-file produced by dft.exe:

n = [0:15];
x = [ -0.763848 0.445723 0.350261 0.036225 -0.217444 0.121921 -0.604968 0.623280 -0.966613 0.806513 -0.387738 0.412580 0.580554 0.705191 0.440352 -0.077792 ];
Xre = [ 1.504196 0.561935 -1.724586 -1.655775 -1.165293 -0.460708 -2.462571 2.365616 -4.643086 2.365602 -2.462494 -0.460714 -1.165152 -1.655658 -1.724676 0.561582 ];
Xim = [ 0.000000 1.771657 -0.359353 -1.262270 -1.085051 -0.100950 -0.105040 -0.259128 0.000203 0.258721 0.105206 0.100861 1.085066 1.262431 0.359592 -1.771775 ];
P = [ 2.262606 3.454538 3.103332 4.334917 2.535245 0.222443 6.075290 5.663288 21.558247 5.663008 6.074944 0.222430 2.534947 4.334937 3.103816 3.454561 ];
subplot(3,1,1)
plot(n,x)
xlim([0 15])
subplot(3,1,2)
plot(n,Xre,n,Xim)
xlim([0 15])
subplot(3,1,3)
stem(n,P)
xlim([0 15])

I ran dftplots.m in Octave, as shown below:

Running the dftplots M-file in GNU Octave

It produced the following graphs:

DFT plots produced by GNU Octave, using data generated by dft.exe

The top plot is the original random discrete-time sequence x[n]. The second plot shows the real and imaginary parts of X[k]. Note the symmetry of X[k], which is just as we expect for real x[n]. The final plot is P[k], the power spectrum of x[n]. Basically, each value of P[k] is the square of the magnitude of the corresponding complex value X[k]. Again the symmetry is just as we expect for real x[n].


Relatively simple DFT example for dsPIC

$
0
0

I’ve been trying to create a simple DFT example for the dsPIC. I found it more difficult than I expected to come up with something which is both useful and readable. I’m intentionally not doing either of the following:

  1. Using the special DSP hardware features of the dsPIC.
  2. Using an FFT algorithm.

Obviously, this could run faster if I did either or both of the above items. However, I’m trying to create an example that’s as understandable as possible, rather than one which is as fast as possible.

This example records a 64-sample frame of data from AN0 (analog input channel 0, pin 2) and then calculates the DFT and power spectrum, up to and including the Nyquist frequency. (Since the input data is real, the frequency spectrum values above Nyquist are redundant.) The program then searches through the power spectrum to identify the frequency bin of maximum power. Bin 0 (DC) is exluded, since the input signal will presumably have a large DC component.

//
// dsPIC DFT Example
// Written by Ted Burke
// Last updated 10-12-2013
//

#include <xc.h>
#include <stdio.h>
#include <libpic30.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
unsigned int read_analog_channel(int n);

// Window length, N (greater than 4 and a power of 2)
#define N 64

// Twiddle factors (64th roots of unity)
const float W[] = {
     1.00000, 0.99518, 0.98079, 0.95694, 0.92388, 0.88192, 0.83147, 0.77301,
     0.70711, 0.63439, 0.55557, 0.47139, 0.38268, 0.29028, 0.19509, 0.09801,
    -0.00000,-0.09802,-0.19509,-0.29029,-0.38269,-0.47140,-0.55557,-0.63440,
    -0.70711,-0.77301,-0.83147,-0.88192,-0.92388,-0.95694,-0.98079,-0.99519,
    -1.00000,-0.99518,-0.98078,-0.95694,-0.92388,-0.88192,-0.83146,-0.77300,
    -0.70710,-0.63439,-0.55556,-0.47139,-0.38267,-0.29027,-0.19508,-0.09801,
     0.00001, 0.09803, 0.19510, 0.29030, 0.38269, 0.47141, 0.55558, 0.63440,
     0.70712, 0.77302, 0.83148, 0.88193, 0.92388, 0.95694, 0.98079, 0.99519
};

int main()
{
    // Configure AN0-AN8 as analog inputs
    ADCON3bits.ADCS = 15;  // Tad = 266ns, conversion time is 12*Tad
    ADCON1bits.ADON = 1;   // Turn ADC ON

    // Setup UART
    U1BRG = 48;            // 38400 baud @ 30 MIPS
    U1MODEbits.UARTEN = 1; // Enable UART

    // time and frequency domain data arrays
    int n, k;                     // time and frequency domain indices
    float x[N];                   // discrete-time signal, x
    float Xre[N/2+1], Xim[N/2+1]; // DFT of x (real and imaginary parts)
    float P[N/2+1];               // power spectrum of x
    int a, b;                     // twiddle indices
    int to_sin = 3*N/4; // index offset for sin
    int k_max;                    // index of max frequency bin
    float X_max;                  // power at max frequency bin
    
    while(1)
    {
        // Record N samples @ 10kHz
        for (n=0 ; n<N ; ++n)
        {
            x[n] = read_analog_channel(0);
            __delay32(3000); // 100us delay
        }
        
        // Calculate DFT and power spectrum up to Nyquist frequency
        for (k=0 ; k<=N/2 ; ++k)
        {
            Xre[k] = 0; Xim[k] = 0;
            a = 0; b = to_sin;
            for (n=0 ; n<N ; ++n)
            {
                Xre[k] += x[n] * W[a%N];
                Xim[k] -= x[n] * W[b%N];
                a += k; b += k;
            }
            P[k] = Xre[k]*Xre[k] + Xim[k]*Xim[k];
        }
        
        // Find and print max amplitude frequency bin
        X_max = 0;
        for (k=1 ; k<=N/2 ; ++k) if (P[k] > X_max)
        {
            X_max = P[k];
            k_max = k;
        }
        printf("Max freq bin: %d\n", k_max);
    }
    
    return 0;
}

// This function reads a single sample from the specified
// analog input. It should take less than 5us when the
// microcontroller is running at 30 MIPS.
// The dsPIC30F4011 has a 10-bit ADC, so the value
// returned is between 0 and 1023 inclusive.
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 = 3.2us
    return ADCBUF0;
}

I saved the program above to a file called “main.c” and compiled it using Microchip’s XC16 C compiler. This is my build script:

xc16-gcc main.c -mcpu=30F4011 -Wl,--script=p30F4011.gld
if errorlevel 0 xc16-bin2hex a.out

By the way, I generated the values for the global array of twiddle factors in the above example using the following short C program (which runs on the PC rather than the dsPIC):

//
// twiddle.c - Print array of twiddle factors
// Written by Ted Burke
// Last updated 10-12-2013
//
// To compile:
//    gcc twiddle.c -o twiddle.exe
//
// To run:
//    twiddle.exe
//

#include <stdio.h>
#include <math.h>

#define N 64

int main()
{
    int n;
    
    for (n=0 ; n<N ; ++n)
    {
        printf("%8.5lf", cos(n*6.2832/N));
        if (n<N-1) printf(",");
        if ((n+1)%8==0) printf("\n");
    }
    return 0;
}

Simple communication with a TCP/IP device using Python

$
0
0

I had an interesting correspondence recently with Wim Bruyn from Holland who was using my SerialSend program to implement a remote-controlled relay system for switching appliances on and off automatically. The relays Wim is using (ETH002) are actually controlled via TCP/IP, so he ended up abandoning SerialSend in favour of a custom Python script. It struck me as a great example of using Python to do automation really easily and I think others will find it useful. Wim has kindly given me permission to reproduce it here. I made some tiny modifications, but Wim did all the work.

Python is available for Windows, Linux, Mac OS and various other platforms. It can be downloaded for free from python.org and only takes a couple of minutes to install. All modules used in the examples below (socket, smtplib, argparse, time) are part of the standard library, so they shouldn’t require separate installation.

I’ll present this example in two stages. The first is very pared back version which demonstrates the basic process of sending a short byte sequence via TCP/IP and then sending a short report via email.

#
# simple_rc.py - Written by Wim Bruyn (slightly modified by Ted Burke)
#
# This short Python program sends a three byte command to a remote
# controlled relay device via TCP/IP. It receives a response indicating
# whether the command was carried out. A report is then sent via email.
#

import socket    # used for TCP/IP communication 
import smtplib   # used to send email report
import time      # used to insert current date in email report

# Prepare 3-byte control message for transmission
TCP_IP = '192.168.2.115'
TCP_PORT = 17494
BUFFER_SIZE = 80
MESSAGE = '\x21\x01\x00' # Relays 1 permanent off

# Open socket, send message, close socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(MESSAGE)
data = s.recv(BUFFER_SIZE)
s.close()

# Create report and send as email
sender = 'joe.bloggs@email.com'
receivers = ['joe.bloggs@email.com']

if data == '\x00':
    msg = 'Device powered off on date: ' + time.strftime(“%c”)
else:
    msg = 'Error: Device not powered off on date: ' + time.strftime(“%c”)

smtpObj = smtplib.SMTP('mailhost.email.com', 25)
smtpObj.sendmail(sender, receivers, msg)
smtpObj.quit()

The second example, shown below, is Wim’s final program. It parses command line arguments, allowing eight relay modules to be switched on or off individually. Wim uses this program in conjunction with the Windows Task Scheduler to automate on/off switching of appliances.

#
# SwitchETH002.py - Written by Wim Bruyn
#
# Platform: python33, Windows 7
#
# Purpose:
#   Switching ETH002 or ETH008 TCP/IP relays on and off and send an
#   e-mail when done. Windows task scheduler can be used to activate
#   the relays (optional).
#
# Command line arguments required for:
#   Relays number (1-8),
#   Mode (on/off),
#   e-mail report ID (free format text).
#
# Example usage:
#
#   c:\python33\python.exe SwitchETH002.py 2 off "External WD USB disk"
#

import socket
import time
import argparse
import smtplib

def SendCommandToRelays (MESSAGE): #Value of MESSAGE is command to be send to relays
    TCP_IP = '192.168.2.115' #IP address of the relays
    TCP_PORT = 17494 #Port number of the relays
    BUFFER_SIZE = 80

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((TCP_IP, TCP_PORT))
    s.send(MESSAGE)
    data = s.recv(BUFFER_SIZE) #Response from Relays
    s.close()

    if data == b'\x00':
        SendMailMessage ( (args.Device + ' is powered ' + args.Function + ', on date: ' + time.strftime("%c") ) )
    else:
        SendMailMessage ( ('Error:' + args.Device + ' is not powered ' + args.Function + ', on date: ' + time.strftime("%c")) )

def SendMailMessage (Mtext): #Value of Mtext is action report send to mail recipient
    sender = 'joe.bloggs@email.com' #mail address of the sender
    receivers = ['joe.bloggs@email.com'] #mail address of the receiver

    smtpObj = smtplib.SMTP(‘mailhost.email.com’, 25)
    smtpObj.sendmail(sender, receivers, Mtext)
    smtpObj.quit()

parser = argparse.ArgumentParser()
parser.add_argument ("Number", help = "Relays number 1 – 8", type=int, choices = [1, 2, 3, 4, 5, 6, 7, 8])
parser.add_argument ("Function", help = "on is relays on, off is relays off", choices = ["on", "off"])
parser.add_argument ("Device", help = "Device id for e-mail message")

args = parser.parse_args()

if args.Number == 1 :
    if args.Function == 'on' :
        print ('Relays 1 on'),
        SendCommandToRelays ( b'\x21\x01\x00' ) #Relays 1 permanent on
    elif args.Function == 'off' :
        print ('Relays 1 off'),
        SendCommandToRelays ( b'\x20\x01\x00' ) #Relays 1 permanent off

if args.Number == 2 :
    if args.Function == 'on' :
        print ('Relays 2 on'),
        SendCommandToRelays ( b'\x21\x02\x00' ) #Relays 2 permanent on
    elif args.Function == 'off' :
        print ('Relays 2 off'),
        SendCommandToRelays ( b'\x20\x02\x00' ) #Relays 2 permanent off

if args.Number == 3 :
    if args.Function == 'on' :
        print ('Relays 3 on'),
        SendCommandToRelays ( b'\x21\x03\x00' ) #Relays 3 permanent on
    elif args.Function == 'off' :
        print ('Relays 3 off'),
        SendCommandToRelays ( b'\x20\x03\x00' ) #Relays 3 permanent off

if args.Number == 4 :
    if args.Function == 'on' :
        print ('Relays 4 on'),
        SendCommandToRelays ( b'\x21\x04\x00' ) #Relays 4 permanent on
    elif args.Function == 'off' :
        print ('Relays 4 off'),
        SendCommandToRelays ( b'\x20\x04\x00' ) #Relays 4 permanent off

if args.Number == 5 :
    if args.Function == 'on' :
        print ('Relays 5 on'),
        SendCommandToRelays ( b'\x21\x05\x00' ) #Relays 5 permanent on
    elif args.Function == 'off' :
        print ('Relays 5 off'),
        SendCommandToRelays ( b'\x20\x05\x00' ) #Relays 5 permanent off

if args.Number == 6 :
    if args.Function == 'on' :
        print ('Relays 6 on'),
        SendCommandToRelays ( b'\x21\x06\x00' ) #Relays 6 permanent on
    elif args.Function == 'off' :
        print ('Relays 6 off'),
        SendCommandToRelays ( b'\x20\x06\x00' ) #Relays 6 permanent off

if args.Number == 7 :
    if args.Function == 'on' :
        print ('Relays 7 on'),
        SendCommandToRelays ( b'\x21\x07\x00' ) #Relays 7 permanent on
    elif args.Function == 'off' :
        print ('Relays 7 off'),
        SendCommandToRelays ( b'\x20\x07\x00' ) #Relays 7 permanent off

if args.Number == 8 :
    if args.Function == 'on' :
        print ('Relays 8 on'),
        SendCommandToRelays ( b'\x21\x08\x00' ) #Relays 8 permanent on
    elif args.Function == 'off' :
        print ('Relays 8 off'),
        SendCommandToRelays ( b'\x20\x08\x00' ) #Relays 8 permanent off

Programming the PIC16F819 on an LCD03 adapter using a PICkit 2

$
0
0

I was looking around the office for a PIC16F microcontroller to try out some sample code, but I couldn’t find any lying around. My colleague Richard Hayes gave me a little circuit board which is an adapter for serial to HD44780 (that’s the widely used Hitachi LCD display interface). It uses a PIC16F819 to do the conversion for serial to the LCD interface and Richard reckoned that one five-pin block of pin connections contained everything needed to program using a PICkit 2. I haven’t actually programmed it yet, but it looks like he’s right.

The adapter board has “LCD03″ written on it and although I can’t find the exact model online, it looks like it’s an older version of the LCD05 adapter shown at the bottom of this page. Whenever I’ve seen one of these LCD03 modules before it has always been attached to an LCD display, like the one shown below which I’ve just come across on my desk.

LCD03 adapter board attached to Hitachi LCD module

I’ll update this post once I’ve programmed the PIC, but for the time being these are the connections for the PICkit 2:

Diagram showing connections between PIC18F819 circuit board and PICkit 2

Link to editable Inkscape SVG version of connection diagram.

Once I had soldered a 5-pin header onto the LCD03 board, this is how I connected it to the PICkit 2:

PICkit 2 connected to PIC16F819 on LCD03 adapter board

To check that the connection was working, I used the PICkit 2 software application to try to read the contents of the PIC’s program memory. As shown below, although the connection was successful, the program memory could not be read because “code protect” is enabled. It remains to be seen whether the chip can still be programmed.

Screenshot of PICkit 2 software application with PIC16F819 connected


Real-time analysis of data from BioSemi ActiveTwo via TCP/IP using Python

$
0
0

JohnAlan (Jack) Keegan and I have been doing some interesting work on real-time electroencephalogram (EEG) analysis recently and we had a really productive practical session in the lab today. The EEG is a recording of the electrical activity of the brain made using electrodes on the scalp. Jack is doing some fascinating work on Visual Evoked Potentials (VEPs), which are patterns that occur in response to visual stimuli and appear in EEG recorded from the visual cortex (at back of the head). Jack is working on new types of visual stimulus which we hope will produce VEPs that can be evoked and detected more easily and reliably.

The BioSemi ActiveTwo biopotential acquisition system

Anyway… The system Jack is using to record EEG is the BioSemi ActiveTwo (shown above), which is a high-end biopotential acquisition system used by many researchers all over the world. It’s expensive and a little bit cumbersome to use, but it really does record biopotentials very well (and does so reliably). For a long time, we have been using the software that comes with the ActiveTwo (called ActiView) to record data during our experiments and then analysing the data offline. For example, Jack would wire up a subject, present several visual stimuli to him/her and record the resulting EEG to files. Only once the experiment was complete would he be able to analyse the data and discover if any of the stimuli worked as expected. ActiView displays the time-domain signals in real time, but it doesn’t perform real-time analysis.

So, we’ve had this long-standing intention to implement real-time data analysis and display (of frequency spectra and the like), so that it’s possible to manipulate stimuli in real-time and see what effect the changes are having on the neurological response. Jack did some work a year or more ago on writing some software to access data directly from the BioSemi libraries, which was close to working but took quite a while to work out. I think we both just kind of ran out of steam on that approach at the time because it was quite fiddly.

What we did today was to access the data a completely different way by connecting to ActiView via TCP/IP (which is something it allows). It turned out to be incredibly easy to connect to ActiView and parse the data packets it streams over the network using Python. As it happens, we were actually running the Python program on the same PC as ActiView, so we accessed it via the localhost loopback interface (which is always IP address 127.0.0.1). All our Python program is doing is connecting to ActiView, which should already be running, appropriately configured and recording data. The program parses incoming data packets, storing only the data from channel 1. Once it has a complete window of data (we chose a window length of 512 arbitrarily) it performs an FFT and plots the spectrum of the signal. It repeats this 50 times, updating the same plot repeatedly.

We didn’t have the BioSemi hooked up to anybody when we tested the program today, so the data we recorded is just noise, but we’re optimistic that it’s working correctly.

This is a snapshot of the DFT (real part only) that was displayed by the Python program using the matplotlib library.

BioSemi_FFT

ActiView needs to be running to actually control the recording. During recording, it provides a scrolling real-time display of the data. In this case, there was no subject connected to the equipment, so it just recorded low amplitude noise:

Signals from BioSemi ActiveTwo displayed in ActiView

This is the TCP Server configuration tab in ActiView. In this screenshot, the Python program has already connected, so the corresponding green light is illuminated. Once you select the number of channels to stream over the network, the information labels at the bottom of the screen show you what packet size will be used and how many samples per channel will be included in each packet. Jack’s Python code is configured to receive 384-byte packets, containing 16 samples per channel for 8 channels. Each sample is 3 bytes long.

Actiview_TCP_Server

Jack runs the Python program within iPython, as shown below:

Biosemi_Console

This is the complete source code for the current program.

#
# test_plot.py - Written by Jack Keegan
# Last updated 16-1-2014
#
# This short Python program receives data from the
# BioSemi ActiveTwo acquisition system via TCP/IP.
#
# Each packet received contains 16 3-byte samples
# for each of 8 channels. The 3 bytes in each sample
# arrive in reverse order (least significant byte first)
#
# Samples for all 8 channels are interleaved in the packet.
# For example, the first 24 bytes in the packet store
# the first 3-byte sample for all 8 channels. Only channel
# 1 is used here - all other channels are discarded.
#
# The total packet size is 8 x 16 x 2 = 384.
# (That's channels x samples x bytes-per-sample)
#
# 512 samples are accumulated from 32 packets.
# A DFT is calculated using numpy's fft function.
# the first DFT sample is set to 0 because the DC
# component will otherwise dominates the plot.
# The real part of the DFT (all 512 samples) is plotted.
# That process is repeated 50 times - the same
# matplotlib window is updated each time.
#

import numpy                     # Used to calculate DFT
import matplotlib.pyplot as plt  # Used to plot DFT
import socket                    # used for TCP/IP communication
 
# TCP/IP setup
TCP_IP = '127.0.0.1' # ActiView is running on the same PC
TCP_PORT = 778       # This is the port ActiView listens on
BUFFER_SIZE = 384    # Data packet size (depends on ActiView settings)

# Open socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))

# Create a 512-sample signal_buffer (arange fills the array with
# values, but these will be overwritten; We're just using arange
# to give us an array of the right size and type).
signal_buffer = numpy.arange(512)

# Calculate spectrum 50 times
for i in range(50):
    # Parse incoming frame data
    print("Parsing data")
    
    # Data buffer index (counts from 0 up to 512)
    buffer_idx = 0
    
    # collect 16 packets to fill the window
    for n in range(32):
		# Read the next packet from the network
        data = s.recv(BUFFER_SIZE)
        
        # Extract 16 channel 1 samples from the packet
        for m in range(16):
            offset = m * 3 * 8
            # The 3 bytes of each sample arrive in reverse order
            sample = (ord(data[offset+2]) << 16)
            sample += (ord(data[offset+1]) << 8)
            sample += ord(data[offset])
            # Store sample to signal buffer
            signal_buffer[buffer_idx] = sample
            buffer_idx += 1
    
    # Calculate DFT ("sp" stands for spectrum)
    sp = numpy.fft.fft(signal_buffer)
    sp[0] = 0 # eliminate DC component
    
    # Plot spectrum
    print("Plotting data")
    plt.plot(sp.real)
    plt.hold(False)
    plt.show()

# Close socket
s.close()


Keyboard shortcut to simulate a mouse right-click in CrunchBang Linux

$
0
0

I don’t have Linux installed on my Windows 8 Asus laptop, so when I want to do some Linux work I usually just boot into a live session from a USB key. My favourite Linux distribution is CrunchBang because it’s really lightweight and Debian-based so I can easily apt-get whatever applications I might need during a live session. There’s just one problem: the right click doesn’t work on my laptop’s touchpad.

If CrunchBang was actually installed on the hard disk of the laptop, I could presumably work out how to configure the mouse properly. However, since I run CrunchBang as a live session, I would need to reconfigure it every time I boot. Whenever I need to right-click, I therefore use the following workaround:

  • I position the mouse where I want to right-click.
  • I type "Alt+F2" on the keyboard to open the “Run program” dialog box.
  • I then run the following command: "xdotool click 3".
Run program dialog used to run xdotool to simulate right click

To simulate a right click in CrunchBang, position the mouse pointer, then press Alt+F2 to open the “Run Program” dialog box, type “xdotool click 3″ and press return.

xdotool simulates mouse and keyboard events in X Windows. Here, I’m using it to simulate a right click wherever the mouse is currently positioned. This works fine for an occasional right click, but if I’m doing something that involves a lot of right-clicking, it’s obviously inconvenient.

To create a keyboard shortcut to simulate a right click, I use the following method.

First, I add the following as line 315 of the Openbox configuration file in my home directory ("~/.config/openbox/rc.xml"):

<keybind key="W-r"><action name="Execute"><command>xdotool click 3</command></action></keybind>

If desired, the config file can be modified automatically using sed to add the new key binding at line 315. This is the command to use:

sed -i '315i<keybind key="W-r"><action name="Execute"><command>xdotool click 3</command></action></keybind>' ~/.config/openbox/rc.xml

A sed command running in a console to edit the Openbox config file

Here’s how the file "rc.xml" looks in geany once it has been modified:

Modified Openbox congif file

To put that into effect, I restart Openbox. This can be done by running the following command in a console:

killall -USR1 openbox

Alternatively, open the Openbox main menu by pressing the "Super+Space" shortcut, then select "Settings->Openbox->Restart", as shown below.

Openbox main menu - restart Openbox

To restart Openbox, press Super+Space to access the main menu, then select “Settings->Openbox->Restart”.

Note that the "Super" key is the the one with the Windows symbol. On my keyboard, it’s immediately to the left of the Alt key near the lower left corner of the keyboard.

I can then right click by pressing "Super+r". For some reason, it doesn’t work on the desktop itself, but it works perfectly in application windows.

TLDR (short version):

To do the whole thing quickly, just run the following commands in a console:

sed -i '315i<keybind key="W-r"><action name="Execute"><command>xdotool click 3</command></action></keybind>' ~/.config/openbox/rc.xml
killall -USR1 openbox

Big Foot Magic Hands

Motion Tracking on the Cheap with a PIC

$
0
0

batchloaf:

Projects by Aron Horan from DIT programme DT009 have recently featured twice on hackaday.com. The first one to be featured was his Dodging Robot, which is pretty nifty, but this one here is an interesting combination of SHARP rangefinder and servo motor. Nice work Aron!

Originally posted on Hack a Day:

motion tracking

Ever need a cheap motion tracker for very basic object following? Did you know you can throw one together with a few IR distance sensors and a PIC?

The setup is fairly simple. [Aron Horan] is using a dsPIC30F4011 PIC, a SHARP infrared distance sensor, an RC servo, and a PICkit2 for testing. It works by scanning left and right using the servo motor. When the edge of an object is detected, it will turn away from the object until it can no longer detect the edge — then it turns back. Unfortunately this does mean it will always be twitching, even when it’s tracking an object.

View original


2D Room Mapping With a Laser and a Webcam

$
0
0

batchloaf:

Shane Ormonde, from DIT programme DT009, also recently featured on hackaday.com. His project is a lovely adaptation of a webcam laser range finder (originally by Todd Danko) to scan and map the shape of a room. It’s an ingenious piece of work – I hope to try this out myself soon!

Originally posted on Hack a Day:

2014-01-29-21-02-40

[Shane Ormonde] recently learned how to measure distance using just a webcam, a laser, and everyone’s favorite math — trigonometry. Since then he’s thrown the device onto a stepper motor, and now has a clever 2D room mapping machine.

He learned how to create the webcam laser range finder from [Todd Danko], a project we featured 7 years ago! It’s a pretty simple concept. The camera and laser are placed parallel to each other at a known distance, axis-to-axis. On the computer, a python script (using the OpenCV library) searches the image for the brightest point (the laser). The closer the brightest point is to the center of the image, the farther the object. Counting pixels from the center of the image to the laser point allows you to calculate an angle, which can then be used to calculate the distance to the object — of course, this needs to be calibrated to be at all accurate. [Shane] does a great job explaining all of this in one of his past posts, building the webcam laser rangefinder.

View original


Phasor diagrams in GNU Octave

$
0
0

I’ve been experimenting with plotting phasor diagrams in GNU Octave. This approach may also work in MATLAB, but I haven’t tried that yet. I need to tidy this up a little more to make it convenient to reuse, but I’m capturing it as it is for the time being, so that I don’t lose it.

GNU octave phasor diagram

This is my M-file, “example.m”:

%
% GNU Octave phasor plot example
% Written by Ted Burke - 19-2-2014
%

% Create a few complex values
z1 = 3 + 3j;
z2 = 2 + 4j;
z3 = z1 * z2;

% Calculate a suitable axis limit based on phasors
m = 1.2 * max([real([z1 z2 z3]) imag([z1 z2 z3])]);

% Create a new figure window
figure
hold on

% Plot phasors one at a time
quiver(0,0,real(z1),imag(z1))
quiver(0,0,real(z2),imag(z2))
quiver(0,0,real(z3),imag(z3))

% Add horizontal and vertical axes
plot([-m m],[0 0])
plot([0 0],[-m m])

% Set axis limits and aspect ratio
axis([-m,m,-m,m], 'square')

To install octave in Linux (debian), just run the following command as root:

apt-get install octave

Then, to run the example:

octave_running_example


Viewing all 72 articles
Browse latest View live