A Circular Reference

Adventures of a vagabond electron.

Real Time Plotting With Matlab

| Comments

Introduction

The need to plot (and filter) digital data from a sensor or front end circuit is ubiquitous in embedded systems. It’s always good to plot/filter/process the data in the same environment, so it’s always good to plot data in Matlab (if you have access to it). This post will talk about how to write a simple .m file to read data from the serial port of a PC and plot it onto a figure. We will do this in “real-time”, kind of like how an oscilloscope does.

Implementation

I assume that you already have a microcontroller or something else that will sample the analog signal you want to plot, convert the HEX values into ASCII and send it over a UART/RS232 interface to your PC. The problem this post addresses is capturing this data from Matlab and plotting it continuously.

The capture part is really straight forward, you just have to create a serial port object, open it and perform fread() on it, like shown below:

1
2
3
4
5
6
7
8
9
10
s = serial('COM1'); %assigns the object s to serial port
set(s, 'InputBufferSize', 1024); %number of bytes in input buffer
set(s, 'FlowControl', 'hardware');
set(s, 'BaudRate', 19200);
set(s, 'Parity', 'none');
set(s, 'DataBits', 8);
set(s, 'StopBit', 1);
set(s, 'Timeout',10);
fopen(s); %opens the serial port
a=fread(s,1,'char'); %read 1 byte to a, make sure that the input buffer size is > the no of bytes you read.

Once you get the data into matlab using fread(), the next problem is plotting this data using plot(). By default, plot() clears old data in the figure and re-draws the figure at every call. To avoid this and to have plot() append data you need to set hold on

Now you have a graph that is updated in real time, with each sample. This might be good enough if your sampling rate is slow (a few 100mS or so); but for a faster sampling rate (mine was 4mS) it is too slow and soon my PC started catching fire. More importantly, the real strength of Matlab/Octave lies in vector operations – and it really does well in handling large chunks of data.

So to improve the performance, we modify the design to `fread()’ 1 second worth of samples and plot all of that data in one shot, every second. Further, to create an oscilloscope effect, one screen will display X seconds of data; after that the screen will be refreshed and another X seconds are displayed 1s at a time. This goes on for Y number of times.

Now ideally, you want Y to be infinite, and the capture should only stop when you press a button or something. So we add a callback function myCallback for the WindowButtonDownFcn event to cause the capture to stop when you click on the plot figure with your mouse.

Notes

While debugging my code I encountered a few problems:

  • If for some reason, your script breaks and you are not able to close the serial port, the next time when you try to run the script you will get a port open error. One way to solve this is the execute the commands
1
newobjs = instrfind ; fclose(newobjs);

at the begining of the script.

  • If matlab does not receive data on the serial port, the object times out and you get a warning. You could catch this by something like:
1
2
3
4
5
6
7
8
9
10
11
lastwarn(''); % reset lastwarn 
try
   a=fread(s,Fs*Ip_Data_Size,'char'); %read 1s data 
   if(~isempty(lastwarn))
       error(lastwarn)
   end
catch err
    disp('Timeout occurred, exiting');
    fclose(s); %close the serial port 
    return; % Stop Script 
end
  • It is also faster to have calculate the X and Y scales statically instead of using
1
axis auto;

With that you should have a nice real time plotter working as in this video. The next step is to add on-the-fly filtering.

The code for the plotter is here. Each input data sample is assumed to be in the form +xxxx- where xxxx are any hex character in ASCII. For example the data samples of 0x123F and 0xD278 would be sent as +123F-+D278- .. and so on.

One Minute Introduction to Graphics TFT LCDs for Embedded Folks

| Comments

Inspired by Jan Axelson’s One minute USB, I have decided to jot down one minute notes for other ridiculously complicated stuff that I deal with every once in a while. First in the series is a TFT LCD note.

  1. A TFT LCD is made up of X x Y number of pixels, each pixel is made up of 3 segments (Red/Green/Blue). The number of bits allocated to R/G/B segments depends on how many bits per pixel (BPP) the display supports. Common formats are 8, 9, 16 and 18 BPP.

  2. Displays can be a bare (RAM-less) glass or it can come with built-in a graphics controller with internal RAM. So the maximum size of display supported by a controller depends on how much RAM it has. For example a controller with 345600 Bytes RAM can support a resolution of 320 x 480 with 18 BPP.

  3. These built-in display controllers generally support a variety of interface “communication” modes. Common formats are 16/8 bit “MCU mode” (also called “System Bus”), which actually means the good old Intel 8080 or Motorolla 6800 parallel bus interface and Serial bus mode(s) (like an SPI using 3 and/or 4 wires). The controller also usually supports a self-abnegating, masochistic mode called RGB mode of operation. In this mode the display controller ceases to exist and allows you to drive the display externally as if it were a bare glass. Which also happens to be what you need to do if your display does not have an controller. You have to provide frame information (HSYNC, VSYNC, Data, CLK..) over the bus. The RGB mode is useful if you are using another external graphics controller (that has more features than the built-in controller) or you want to be flexible to changing the display glass, and perhaps also for lower cost. However, do note that before you enter RGB mode, you need to configure some of the controllers registers with appropriate parameters, and you have to do this using one of the above “communication” modes. The serial modes would be most efficient for this since the data to be transferred is small and need only be sent once on initialization. You can choose which communication mode to use by setting a couple of pins in the display interface high or low.

  4. The color depth of the display depends on the BPP. For instance if you have 18BPP, the color depth is 2 * exp(6+6+6) = 262k colors (assuming 6 bits each for Red, Green and Blue). And if it’s a 1BPP then you can only have 8 colors. 16BPP gives 65k color and this is the most common scheme used, since it can fit nicely into a 16 or 8 bit parallel bus.

  5. The initialization code for a display controller is generally provided by the manufacturer of the display glass and is application/controller specific. However there seem to be some common features across all controllers. For example the addressing of pixels usually have row and column start and end pointers, so in practice it’s not too difficult to write your own init code if you refer some similar controller’s code.

  6. The modus operandi to write a pixel into an arbitrary location in RAM is to a) set Column and Row address pointers (SC,SP) to the (x,y) co-ordinates you want to write the pixel to b) write the 16 bit pixel value to the controller memory Repeat step b) for all the contiguous pixels you want to update. The approach is similar to Read a pixel, only difference being that write control signals are changed to Read signals.

In the next post I will illustrate these concepts with PIC32′s PMP module and two display controllers – ILI9486 and HX8347G.

A Simple I2C Driver for PIC32, PIC24 and PIC18

| Comments

Introduction

The Microchip I2C drivers and their APIs that ship with the MPLAB compiler are almost impossible to debug or understand. For some unfathomable reason, they have distributed the I2C access code in 10 (or more) different files with 5 lines of code each. So I’ve managed to put together a simple (and what I consider to be intuitive) driver. I have tested it with different slaves on PIC18, PIC24 and PIC32, and it seems to work seamlessly with only a change in .h file #defines for PIC24 and PIC32, while for PIC18 some register names are different.

Before you start, you should be aware that the PIC I2C engine cannot queue commands. So you have to wait in software for each command to be completed before issuing the next one. For example, you issue the start condition command then wait till the engine is not busy before issuing the next command.

Details

The driver implements the following interfaces:

  • Reading from the I2C peripheral:
1
2
3
BOOL ReadRegisterLoop(UINT8 reg, UINT8* rxPtr, UINT8 len) //(PIC18)

BOOL drvI2CReadRegisters(UINT8 reg, UINT8* rxPtr, UINT8 len, UINT8 slave_adr) //(PIC24/32)

This Writes the Regiser Number (reg) on to the slave, and then issues continuous Reads for a total of len times. Typically the slave will just have one byte of data per resister, in which case you just have to pass len = 1. If the slave stores more than one byte in a perticular “register” location, you can read out that too. All the data is copied to the rxPtr. slave_adr is the 7-bit I2C address of the slave device. Internally, the function also waits for some time (100 iterations of a loop) for the slave to generate an ACK, in case it is slow to respond. You can modify this to suit your peripheral.

  • Writing to the I2C peripheral
1
2
3
BOOL WriteRegLoop(UINT8 reg, UINT8* rxPtr, UINT8 len) //(PIC18)

BOOL drvI2CWriteRegisters(UINT8 adr, UINT8* data, UINT8 len, UINT8 slave_adr)  //(PIC24/32)

Write is a bit simpler than the Read. You write the resigter number you want to write to, wait and repeat if there is no ACK. Once you get an ACK, just dump the data byte by byte on the poor old slave.

For most I2C slaves, this is all the access you need.

To port the driver, change the register mapping section in the .h file, along with the Slave address, and then configure the correct baud rate. These sections are highlighted below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#define TPI2CCON    I2C1CON
#define TPCONbits   I2C1CONbits
#define TPI2CSTAT   I2C1STAT
#define TPSTATbits  I2C1STATbits
#define TPI2CMSK     I2C1MSK
#define TPI2CRCV     I2C1RCV
#define TPI2CTRN     I2C1TRN
#define TPI2CADD     I2C1ADD
#define TPI2CBRG    I2C1BRG

#define C_TP_ADDR   0x3E
#define C_TP_READ   0x7D
#define C_TP_WRITE  0x7C

// calculate baud rate of I2C
#define Fosc       (GetSystemClock())
#define Fcy        (Fosc/2)
#define Fsck       100000
#define I2C_BRG    ((GetPeripheralClock()/(2*Fsck))-1)

The code can be downloaded here. The PIC18 driver includes code to communicate with MLX90614 Infra Red Temperature Sensor and implements the SMBus protocol including the calculation of CRC. So you can use it to modify all of the MLX registers, not just read temperature.

An example of the usage is:

1
2
3
4
5
6
7
8
9
10
11
12
#define LIS_I2C_ADDRESS             0x19
#define LIS_ID_REG                  0x0F
#define LIS_CNTRL_REG1              0x20 

drvI2CInit();   // Initialize the i2c module

UART2PutString("Reading ID reg..");
drvI2CReadRegisters(LIS_ID_REG, &temp, 1, LIS_I2C_ADDRESS); // Read the ID register
UART2PutHex(temp);
UART2PutString(".. is the ID");

drvI2CWriteByte(LIS_CNTRL_REG1, 0x00, LIS_I2C_ADDRESS); // Write some register

A Simple Embedded Timer Pattern

| Comments

Introduction

One of the first things I do when I’m programming a new Microcontroller is to set up a timer for the system. Once you have the timer registers properly configured, the next problem is to measure time intervals, and worrying about overflows. The following pattern is the most elegant way of measuring timer intervals without giving you a headache that I have ever seen, and I reuse it all the time!

Problem

Need a method of measuring time intervals for all kinds of application modules. Simple time stamp difference does not work if the timer overflows.

Prerequisite

You have configured your (hardware) timer to generate a periodic interrupt every X seconds (or milli seconds or whatever). Choose the tick so that it provides at least the smallest resolution you need for your system.

Solution

  1. Inside the timer interrupt, increment a unsigned global variable, let’s call it FreeRunningCounter.

  2. To measure time difference, simply take a Time Stamp of the FreeRunningCounter at the start of the activity. The difference between the TimeStamp and the current FreeRunningCounter modulo the width of the hardware timer is the time elapsed. This will work so long as the measured period is less than or equal to the full range of FreeRunningCounter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
UINT32 FreeRunningCounter;

void Timer_ISR(void){
FreeRunningCounter++; // In the ISR, just increment the counter
}

UINT32 GetTimeStamp(void){
// Achtung: If the word length of the variable FreeRunningCounter is greater than the data bus width of the processor,
// you should disable the timer interrupt before returning the timestamp, since the ISR could interrupt the read of FreeRunningCounter
// and end up modifying bytes of FreeRunningCounter while you are reading it.
#if THIS_IS_AN_8BIT_MACHINE
UINT32 time;
DisableTimerInt();
time = FreeRunningCounter;
EnableTimerInt();
return (time);
#elif THIS_IS_AN_32BIT_MACHINE
return(FreeRunningCounter);
#endif
}

// Elsewhere in the project
UINT32 timeStamp = GetTimeStamp(); // At some time take a snapshot
// ....... run a lot of code here
deltaT = (GetTimeStamp() - timeStamp) & 0xFFFFFFFF; // Gives you the time difference without overflow