Wednesday, January 25, 2012

nRFCam

The nRFCam is an open-source 2.4GHz wireless camera built using the TCM8230MD camera, an ARM Cortex-m3 LPC176x MCU and a Nordic nRF24l01p chip for the radio link.

Overview
On reset, the Nordic chip is initialized and a PWM channel is configured to generate the clock signal for the EXTCLK, fast GPIO is used for the data bus and the other sync signals. Once the camera is configured, using the I2C interface, it starts sending frames over the 8-bits wide data bus as usual, synchronized by the HSYNC, VSYNC and DCLK signals (for more details on this see the TCM8230MD-Breakout post).


The VSYNC interrupt handler is written entirely in assembly, unfortunately this makes it difficult to port but it's much more optimized.  Each scanline is read in a loop and converted from RGB565 to grayscale and then stored in the frame buffer.

Once we read a complete frame, the frame is compressed using lzf and then sent out over SPI to the nordic radio chip. A simple frame header precedes the frame data, consisting of a start of frame marker (0xAA), to synchronise the frames, followed by the length of the frame and the frame data.

Timing
The datasheet does not mention how the data bus clock (DCLK) frequency relates to the external clock (EXTCLK) frequency, however, the maximum DCLK frequency can be inferred from the timing characteristics and from the following diagram, given that the minimum setup time TSU and hold time THD is 10, if the camera is running at the maximum external clock (EXTCLK) frequency which is 25Mhz, then the maximum DCLK frequency is 1s/20ns = 50MHz.
If you sample the data at the rising edge of DCLK it means you have only 10ns to read DOUT, if the MCU is running at 100MHz that's one clock cycle to read DOUT, which is not possible, however, since DCLK is a function of EXTCLK lowering EXTCLK lowers DCLK and increase the time window we have to read the data. The NRFCam generates a 6Mhz EXTCLK for the camera, which gives us a few cycles to read the data.

Compression
The Nordic chip has an on-air datarate of 2Mbps or 250KBps (ignoring any  protocol overhead), it means that even with the camera set to output the smallest possible frame 24KB (128x96x2), the maximum we can send is 10 FPS. In other words, there's no point of trying to keep up with a higher frame rate when all you can send is 10FPS.

The frame is converted from RGB565 to gray scale reducing the frame size by half (12KB), since we don't have enough time between each DCLK edge, the whole scanline is read first and then converted before the next scanline starts (before the next HSYNC edge).

After the whole frame has been read, the frame is compressed using the lzf compression algorithm, there's no particular reason for selecting lzf other than that it was the easiest to port and configure for a low memory footprint, maybe there's an algorithm that can better exploit the data, anyway, at this point the frame size is reduced to 4KBs-6KBs (depending on the frame).

On the other side, the frame can be received by any compatible Nordic chip, decompressed and the grayscale level of each pixel is repeated in the RGB components of the new pixel. Note that since the blue component has 6 bit, in an RGB565, you will need to multiply the grayscale value by two, or left shift by one, for the blue component.

The Prototype
I haven't tested the new board yet, so it's not in the repository. There's however a new breakout, the one used in the prototype, the new breakout is basically the same except that this one is meant to be connected as module, not to the breadboard, and I've removed the crystal oscillator and instead exposed the EXTCLK pin to be controlled by a PWM. This is the prototype in action:
Eagle files and source code
hg clone https://code.google.com/p/nrfcam/
Read more ...

Monday, October 24, 2011

Cortex-M3 Exception Vector Checksum

The 7th entry of the Cortex-M3 exception vector is reserved for the 2's complement of the checksum of the exception vector. At run time the bootloader computes the checksum again and adds it to the one stored in the exception vector, if the result equals zero it starts executing the user code.

The checksum is usually computed by the software that flashes the binary like FashMagic or openocd, if you're using openocd like me, you may see this message every time you flash a binary:
Warn : Verification will fail since checksum in image (0x00000000) to be 
written to flash is different from calculated vector checksum (0xeffc59e6).
Warn : To remove this warning modify build tools on developer PC to inject 
correct LPC vector checksum.
The code will run normally, because openocd computes the checksum for you, but it's just too annoying, so I wrote the following small utility to compute the checksum and inject it into the binary, it's called from the Makefile before running openocd to flash the binary:
#include <stdio.h>
int main(int argc, char **argv)
{
    if (argc == 1) {
        printf("usage: cm3_checksum <bin>\n");
        return 1;
    }

    FILE *file;
    if ((file = fopen(argv[1], "r+")) == NULL) {    
        return 1;
    }

    /* The checksum of the exception vector */
    unsigned int i, n, checksum=0;
    for (i=0; i<7; i++) {
        fread(&n, 4, 1, file);
        checksum += n;
    }

    /* The 2's complement of the checksum */
    checksum = -checksum;
    printf("checksum: 0x%X\n", checksum);
    
    /* write back the checksum to location 7 */
    fseek(file, 0x1c, SEEK_SET);
    fwrite(&checksum, 4, 1, file);

    fclose(file);
    return 0;
}
Read more ...

Tuesday, July 12, 2011

Delay Slots

Delay slots are an artifact of some early pipelined architectures in which  pipeline hazards were not handled explicitly. I was puzzled for while by some unexpected assembly produced by gcc while working on my  own implementation of the MIPS ISA,  further investigation yielded the following results about the branch delay and the load delay slots, both of them occurred in early MIPS architectures.

Load Delay Slot
The load word instruction (lw) loads a word from memory to the specified register, because of the pipelined nature of the architecture, the next instruction(s) execute concurrently with the current instruction,  if the following instruction uses the lw destination register as  one of its source registers then it cannot continue before the lw data is fetched from memory and written back to the destination register, otherwise, it will read invalid data. For example, in the following code snippet, the add instruction uses $s0 as one of its source registers, if it were to read $s0 before it's written by the lw instruction it  would read the old value of $s0.
main:
        lw $s0, 4($0)
        add $s2, $s0, $0

This peculiarity is called a Hazard, specifically a data hazard. Data hazards can be handled by either stalling the pipeline or with register forwarding. The pipeline can be stalled with a nop,  which is sometimes called a bubble because it propagates through the whole pipeline causing every stage to be idle. Register Forwarding, on the other hand, forwards the result of an instruction from the current stage to the previous stage (i.e to the next instruction) bypassing the pipeline, the following image shows register forwarding from the first instruction to the second and third, the following instructions can read the register normally:

If the hazard is not handled by the data path, the assembler introduces a delay slot which it later fills with either a nop or by re-ordering the instructions, if it can find something useful to fill in the delay slot with. This is what the disassembled code looks like, the assembler fills the delay slot with a nop:
00000000 <main>:
   0: 8c100004  lw s0,4(zero)
   4: 00000000  nop
   8: 02008820  add s1,s0,zero

If we change the  add instruction  such that it's not dependant on the lw instruction any more, therefore it can execute in parallel, the assembler removes the nop:
00000000 <main>:
   0: 8c100004  lw s0,4(zero)
   4: 02408820  add s1,s2,zero

Branch Delay Slot
Similarly, due to the parallel execution of the instructions, by the time the branch target gets resolved the following instruction would have been fetched, in  other words, the instruction following a branch always executes whether the branch is taken or not.

This type of hazard is called a control hazard, modern architectures use a branch predictor to avoid flushing the pipeline, it gets flushed only in the case of a branch misprediction. Early MIPS didn't handle this, obviously, and the assembler needed to introduce yet another delay slot and fills it with either a nop or, if possible, by re-ordering the instructions. In the following example, if the branch were to execute without a delay slot it would execute the last addi instruction at each step of the loop which would never finish (because $s0 and $s2 are both incremented)
loop:
        addi $s0, $0, 1 
        add $s1, $s0, $0
        bne $s0, $s2, loop    
        addi $s2, $0, 1

Note that the second add instruction does not affect the branch target so its order is irrelevant and can come before or after the branch, the following disassembled code shows how the assembler re-ordered the instructions and used the add instruction to fill the delay slot:
00000000 <loop>:
   0: 20100001  addi s0,zero,1
   4: 1612fffe  bne s0,s2,0 <loop>
   8: 02008820  add s1,s0,zero
   c: 20120001  addi s2,zero,1
If the branch was, otherwise, dependant on the add instruction, its order will  not be changed and the assembler will use a nop to fill the delay slot instead. If we modify the and instruction and have it write to $s2 making the branch dependant on the and result it will use a nop:
00000000 <loop>:
   0: 20100001  addi s0,zero,1
   4: 2012ffff  addi s2,zero,-1
   8: 1612fffd  bne s0,s2,0 <loop>
   c: 00000000  nop
  10: 20120001  addi s2,zero,1
Read more ...

Saturday, April 30, 2011

Pimp My Hexbug!

What is a Hexbug you ask ? Well, a Hexbug is a line of micro robotic creatures! Sounds fancy doesn't it ? actually it's quite boring, if you ask me, for example, mine just walks around until it hits something and then it turns around and that's basically just about it!
That's why I've decided to reuse the mechanical parts and boost the bug a bit.  So I designed a small wireless board to control my Hexbug. This is the modified Hexbug, I call it nrfbug


Now let's get down to the glorious details...

Wireless Link
For the wireless link I used the nRF24L01+ chip from Nordic. This chip is by far the most amazing VLSI chip that I have ever seen, it certainly deserves it's own post, however briefly, the nRF24L01+ chip is a 2.4Ghz wireless chip that implements a packet-based low level datalink layer protocol (similar to Ethernet) with dynamic payload length, auto-retransmission, auto-ack, CRC, FIFOs,  multiple transmitters, multiple receivers (broadcast). Newer chips even has an integrated USB controller, an improved 8051 core and an AES engine! it's just amazing!

I wrote a library for this chip, originally wrote it for the LPC1768, it's still a work in progress, but it does the job, link in downloads section.

Processing
For the MCU I used an atmega328 running on the internal RC oscillator at 8Mhz, the board has the SPI interface broken out to the header, it's not really compatible with any programmer, that I know of, I just use avrdude and an FTDI chip to bitbang the ihex file to flash.

Motor
On board is an H-bridge to control the motor direction, when the current flows in one direction the nrfbug moves both sets of legs, when it moves in the other direction it moves just one causing it to rotate. The bridge has fly-back diodes for protection. The h-bridge is controlled with two GPIO pins on the MCU.


Sensors
While I was at it, I throw in an SMD ambient light sensor. The sensor is quite simple, it's basically just a light-sensitive transistor that is read by the ADC.

Control
At the other end, I use an mbed to send commands to the bug. Connected to  the mbed is another Nordic chip and a joystick connected to the ADC to move the bug around.
Board
The nrfbug board was designed using Eagle and fabricated at BatchPCB.  The first board had a small problem with the chip antenna having ground pours beneath it, according the datasheet it shouldn't! that's the bless of reading the datasheet after you finish your project :), it only affects the range though (and maybe cause more packets to drop), anyway, I fixed it and waiting for the new revision. The new Eagle files are available in the downloads.


nrfbug
Finally, the bug in action
Downloads
nRF24L01p avr library
hg clone https://code.google.com/p/nrf24l01p
nrfbug Eagle files
hg clone https://code.google.com/p/nrfbug
Read more ...

MCP9800 Temperature Sensor

A while ago I picked up a few temperature sensors from DigiKey, it's been in my junk box for sometime now, so I decided it's about time to do something with it!

Features
The MCP9800 is a high accuracy digital temperature sensor from Microchip, the sensor has an  I2C interface, a configurable 9-bit to 12-bit temperature resolution, shutdown mode, one-shot mode (one conversion while in shutdown) and finally an interrupt pin.

Typical Application
The MCP9800 requires a few external components, the standard I2C pull-ups and, depending on the polarity of the ALERT pin, a pull-up/down resistor.


ALERT pin
The ALERT pin gets asserted when the temperature exceeds the upper temperature limit (TSET register) and again when it falls back below the lower limit (THYST register). The interrupt must be cleared by reading any register.

Application
I used the MCP9800 in a wireless sensor network using nrRF4L01+ and atmega328, each node sends a unique id followed by the temperature reading to a central receiver which then sorts out the data and displays it.


Downloads
MCP9800 avr library (the library has a nice native avr I2C example)
hg clone https://mcp9800.googlecode.com/hg/ mcp9800
Read more ...