Thursday, 14 August 2014

PSDR1 Virgin board has arrived....

...and it's frickin' tiny! A little bigger than a RasPi.

In case you're unfamiliar with this project, look here.


I'll be posting more pics as the build progresses...

Friday, 25 July 2014

The "Hornet": Simple medium-power experimental FM transmitter with common parts

There has been a proliferation of FM "bugs" across the usual suspects of late, all based on rather similar designs, using common components. Wild promises are made about the range and stability of these things, like a few hundred metres. I built a few of them and only one even managed to penetrate a wall, let alone get a few hundred metres.

So, I did what any self respecting hacker would do... and improved it.

WARNING AND DISCLAIMER
  • This circuit is almost certainly illegal to operate in your jurisdiction without an FM broadcast license due to its output power and signal characteristics. 
  • This circuit is provided for educational purposes only and I take no responsibility for interference caused by your build.
  • You should not attempt to build this unless you have some RF experience. 
  • Do not touch the antenna or LC tank (C3/L1) while the circuit is powered. 
  • Do NOT operate this circuit within 10 mi of an airport, unless you know what you are doing! There is no compressor and therefore overdriving the input (eg driving it from a computer line level output at full volume) may cause the signal to spill over into the Airband. This circuit has more than enough power to reach airplanes on approach to an airport and penalties for interference can be severe.


So this design is based on the Talking Electronics "Wasp" with a few important modifications.

The Wasp uses the same three small-signal, low-power transistors. They have an fT (transition frequency) of about 300MHz so can work in all manner of bands, but they cannot switch much current and hence the output power is in the range of a couple of milliwatts. The Wasp will put out probably 20-25mW - that's being generous.

However, the final transistor (Q3) in this circuit is the common BD139 NPN medium-power transistor common in ham QRP equipment. These are pence and are in pretty much every well stocked junk box or parts drawer. The 2SD882 is an even better choice but unless you're in Asia they are a bloody nightmare to obtain.

Now, I don't have access to an RF wattmeter right this second, but at a distance of 1.6 miles (2.5km) an RTL-SDR was still showing ~-30dB with crystal clear audio, with a hill and a housing estate in the way. Extrapolating from previous experiments this suggests an output power of at least half a watt if not quite a bit more.

The compromise is a lower (but still impressive for an audio transistor!) fT of about 90MHz, above which, the acheivable gain starts to drop off rapidly. Below that, though, well, impressive stuff happens!

The BD139 is used as a Class A amplifier and also acts as a buffer, decoupling the antenna from the LC tank which was a major problem with the circuits linked at the start of this article (which are, at their most basic, just FM exciters); the direct coupling from LC tank to antenna caused nearby objects to chance the capacitance of the circuit as a whole and it would start to drift more than a Skyline with wheels made of butter.

This is another idea taken from the Wasp; with this arrangement it still drifts a little but nowhere near the "oh god where the hell has that carrier disappeared to now" levels of the exciter-only circuits. It's enough to shift the carrier off by a few kHz at most but this will still stay in the pass band of most tuned WFM recievers.

Whereas all these bugs use little electret microphones, I find they perform unsatisfactorily even with the gain turned all the way up, so I've just stuck on a line level input. To keep things simple I have chosen to downmix into mono; this part is actually optional and you can just use the ground and left channel as inputs and leave the right channel floating.

I chose a Diamond SRH-771 for the antenna because I have one to hand that recently broke on me. While it's not even nearly resonant at low VHF it does provide satisfactory performance. A "rabbit ears" dipole 1.5m long is best though, or if you're really going for it, why not a yagi? ;)

Please excuse the state of L1, this thing started out at 122MHz and have you ever tried to re-wind a coil soldered into a board?
Contrary to Colin's advice on the original, you can very much build this on matrix board and it will hum along quite happily.

I mounted mine in an old dead laptop PSU casing I had laying around, there is exactly enough space for the heatsink and the battery to hold each other in by friction. The antenna is superglued to the top, although I'll be modding the case to be a bit more slick when I get the chance.

The coil is five turns of .51mm enameled copper wire using an M5 bolt as the former, which I call the 555 rule (no relation): in combination with a 33nF ceramic NP0 capacitor (with a black top) this combo usually sits around 84-88MHz. My first build had 7 or 8 hence the higher frequency.

An NP0 cap isn't strictly necessary but the LC tank will drift less.

Anyway, as I test this more I will post results, but it's an exciting piece of kit to build; if you live in the middle of nowhere, and are daring enough, build it let me know how you get on!

Sunday, 6 July 2014

I made an amplifier.

Nothing special, just an LM386 according to the reference design, with one or two minor mods:

  • The VIN pot's value doesn't actually matter that much. It's a potential divider. All the value will affect is how much the volume will roll off. I stuck in a 4.7K linear pot and it's feeble but enough.
  • I can't stress this enough: check your grounds! I got a LOUD buzzing noise the first time I powered it up; transpired my input ground (hanging off the 10K pot in the schematic) had come loose. Oops.
  • This thing is noisy. Like, tube amp noisy.
  • That Pin 7 bypass cap is not optional. Well, it is, but it shouldn't be. If you omit it the 386 will motorboat (oscillate) like a washing machine with a bad brick habit.
  • The pot I have seems to be dying a slow death, not only is it stiff, but there seems to be a short in there somewhere, because the op amp starts slamming into the rails with quite some force - acting as a voltage comparator instead of an amplifier - if you so much as knock the pot a little bit. I think I lost high frequency hearing in one ear and a heaphone driver finding this out...
  • I added a series RC filter between 1 and 5 for bass boost; I didn't have a .033uF capacitor to hand - of course, TI had to choose the one value I didn't have - but I did have a .047uF polyester cap, which seemed to work just as well. Doing the math it will push the bass boost down from 190Hz to around 140Hz (frequency decreases as capacitance increases) but that's not a problem for me.

    These are the values I used for the lowpass:

     
So why the sudden interest in audio op amps? I'll eventually get round to building a PixieII to supplement my growing collection of homebrew QRP transceivers. (Which currently stands at one. :]) On top of that I managed to salvage a speaker from an old TV which will be sent to a WEEE guy soon (who will be bewildered as hell to find half the board desoldered and lacking of RF components...)

(I'll post a pic of the finished project as soon as I get the camera back...)

Oh and finally, the eagle eyed among you will notice the change in design - I was getting sick of grey and orange, so I've gone for something a bit more minimal. Hope you like!

Sunday, 29 June 2014

SPI (mis-)Adventures: Probing the LIS302DL 3 Axis Accelerometer

The Effing Datasheet

Some stuff about the LIS3xxDL



Image courtesy WejP, source here.

I said I'd get around to doing SPI on this thing, so here it is. This blog seems to be gaining viewership and interest so for once I'm actually going to continue posting, hah :)

The STM32F407 Discovery board contains an on board ST LIS302DL 3-axis MEMS accelerometer. MEMS technology - short for microelectromechanical sensor (try saying that when you're drunk)- is a pretty cool application of physics. You see, tiny (a few μm in size) metallic plates are placed a few
μm apart, and changes in capacitance caused by their movement are read by a tiny ADC and digitised, allowing a wide range of sensors that have traditionally been rather unwieldy to place on a small board to be shrunk down to a few millimetres across.

The board also contains a MEMS microphone that operates on the same principle, although its output is digital PDM (pulse duration modulation) which currently I have no clue how to decode. Whereas a traditional electret mic is a cm or so across and a good 5mm deep, the MEMS mic on the board is 3x4x1mm. You could fit four on a little fingernail with room to spare!

Trivia moment: The LIS301DL was the accelerometer in the original iPhone and still features in a number of Android smartphones, if the name seems familiar.

The device is accessed over a serial peripheral interface bus, and ChibiOS comes specifically with a library for accessing the LIS302DL on the Discovery board.

But, I'm learning SPI on the STM32 in general, so where's the fun in using a black box library to access hardware I could very well poke at myself? Balls to that. :)

Looking at the LIS302DL library & MEMS example that comes with ChibiOS, it seems the following things happen:
  • SPI is initialised, and SPI1 selected.
    SPI1 is the SPI channel that the accelerometer is on. Remember that SPI devices can be daisychained and that only one extra wire (for Chip Select) is needed for each SPI slave; all the SCLK/MISO/MOSI lines can be tied together. That's a subject for a future post, though.
  • The LIS302DL is initialised.
    We can see from the MEMS example that 0x43, 0x0, 0x0 are written to registers 0x20, 0x21 and 0x21 (the LIS302DL's control registers). Looking at the datasheet, 0x43 (0b01000011) in the first control register corresponds to:
    • Bit 1: Data rate (0 = 100Hz)
    • Bit 2: Power down (1 = Off)
    • Bit 3: Full Scale (0 = default)
    • Bits 4 & 5: Self Test Mode (0 = off/normal)
    • Bit 6, 7 and 8: Z, Y and X channels enabled (011 = Y and X only).
  • (As an aside, 0x73 is probably a better choice for us - 0b01000111 - since this enables the Z axis as well.)
    We don't use interrupts in our application so we can leave CTRL_REG2 and CTRL_REG3 alone.
  • We read out the values.These are stored in registers 0x29, 0x2B and 0x2D.
Looking at this from an SPI point of view, the chip accepts two bytes over the MOSI line, and feeds back two bytes over MISO. The first byte in each case is the register in question with the second being the data; we can read out a register by passing 0x80 with the register as byte 2.

Also note that the example deasserts /CS (spiUnselect(&SPID1);) after each transaction. This is important, otherwise the chip's SPI interface will think any further bytes are a continuation of the first command and you will get back 0xFFFF from the chip (which I'm presuming is an error state).

I have posted the menu module here, to demonstrate how to read out values from the accelerometer.

Much of it should be pretty damn obvious; you should ensure SPI is enabled in the HAL and ChibiOS config headers within your project directory.

One thing not immediately intuitive is the control register values in the SPIConfig struct. Fear not, young padawan, for on page 901 of the monster that is The Bible of STM32F4, also known as RM0090: STM32F4xx Reference Manual, lists all the possible values we can put in SPI_CR1. These can be OR'd together, and are explained in further detail in said document. The interesting ones, if we're using 3-wire SPI like any sane human being, are:
  • SPI_CR1_LSBFIRST
    Set if least significant bit is transmitted first; otherwise, MSB is first (which is the case with a majority of SPI-interfaced chips.)
  • SPI_CR1_BRn (where n is 0, 1, 2)
    Set bit rate as a fraction of the peripheral clock rate (fPCLK), with 000 being the fastest and 111 being the slowest. You will generally find the maximum speed in the slave device's datasheet; alternatively, start at 111 and decrease by 1 (you do know how to count in binary, right?) until things stop working. :)
  • SPI_CR1_CPOL, SPI_CR1_CPHA
    SPI clock polarity and phase. The datasheet of the slave device will tell you which you need to use, sometimes devices (such as the AMIC 24L040 EEPROM) behave differently depending on clock phase and polarity.
 Another quick note that I should make is that you would usually put the spiStart function in main() and the SPIConfig as a static const SPIConfig cfg in the global scope, but I wanted to keep everything in one place for the sake of this post. It all seems to work fine, though. If SPI is enabled in the HAL config, I believe spiStart is automatically called when the HAL is initialised. But don't take my word for it.


So now you should have enough information to start poking at SPI devices for yourself. I'm not going to worry too much about the mic, because I intend to use the ADC to read an analog microphone anyway, but next I intend to figure out how DMA works on this chip, since polled SPI is going to be slowww with the ADC I intend to use.

Saturday, 28 June 2014

USB CDC, The ChibiOS Prompt, and the FPU

Three topics in one post! Aren't you a lucky reader? :)

USB Serial, or Why The USB-IF Deserves a Special Place in Hell

If you're used to simple TTL/RS232 serial, USB is a whole different ballgame. Here's the thing; it was not designed to be an easy bus protocol to program with. With all that flexibility, and the user-shalt-not-configure interface, comes a heap load more complexity:

Serial:
  • Connect wires.
  • Set baud rate.
  • Set databits.
  • Set parity.
  • Set stop bits.
  • Write a byte to/read a byte from a register. BOOM. Done.
USB:
  • Figure out how many endpoints you need.
    • Generally, one each for interrupt in/out, one each for bulk in/out.
  • Create the endpoint descriptors.
  • Create the configuration descriptors (includes VID:PID pair, manafacturer, product name, configuration, sub-interfaces, etc.)
  • Configure endpoint callbacks for send, receive, reset, probing, suspend, wakeup, stalling, etc.
  • Set up interrupts.
  • Cycle the bus (disconnect, wait, reconnect).
  • Pray it shows up.
  • On the host:
  • Enumerate all the USB devices.
  • Claim the one you want to communicate with.
  • Repeat steps 1-5.
and if you're lucky, you might have a connection to a device.

Dear lord. Suffice to say, you don't want to deal with this stuff yourself if you have never programmed USB before. Besides some experiments with LUFA, I haven't.

But I still wanted to cut the Arduino out of the development setup, because I wanted it as an SPI slave for future experiments, and it's just another piece of kit to lug around otherwise.

So since I'm using ChibiOS for the moment, I decided to simply strip the ARMCM4-STM32F407-DISCOVERY-MEMS demo of all the crap (stuff to do with the accelerometer, PWM, and so on) and simply reduce it down to the USB Serial initialisation and menu stuff.

See, that demo comes with a cute little prompt, from which you can build on and run experiments:


I don't think I even need to provide example code for this one; all you need to do is write an appropriate callback function with the prototype

static void cmd_your_callback_function_here(BaseSequentialStream *chp, int argc, char **argv)

   //and your code goes here!
}

and add it to the commands table:

static const ShellCommand commands[] = {
  {"mem", cmd_mem},
  {"threads", cmd_threads},
  {"test", cmd_test}, 

  {"your_command_here", cmd_your_callback_function_here},
  {NULL, NULL}
};


Once you've done that, simply connect Minicom to the appropriate serial port (for me it's /dev/ttyACM0, or /dev/ttyACM1 if I have the Arduino attached), and hit Enter for the ch> prompt. help will list all the available commands:


There's not much more to it than that, really! test will run the inbuilt ChibiOS test suite and benchmark, info, mem, menuitem and led are demonstrated above (the led example toggles the given LED on Port D). threads shows the currently running threads (think processes on a Linux or Windows system) and systime will show the number of milliseconds since reset/power on.

What is hf, I hear you ask?

Part of the main attraction of these chips is their brilliant floating point support, something not many microcontrollers have. Since I'm going to be throwing around a lot of floating point numbers (complex numbers, FFT, square roots and such) the FPU is pretty much essential, and the STM32F4 series is pretty much the only ARM microcontroller line out there with hardware FPU/DSP support.

AVR and the like emulate FP numbers using fixed point representation, which I'm not going to go into here, but you can read up yourself.

hf (short for hardfloat, referring to the ARM VFP) simply runs a series of tests on the floating point unit, such as a straight divide -- including division by zero, but I'll get to that in a minute -- and also calculates some arctangents and square roots (which also involve division behind the scenes).

Curiously, if I set the denominator to 0, I get

22/0 = 2147483647.83647

as the result. Is this a bug in the STM32 I wonder? I think I need to read up a bit more on how to get this thing to respond to fault conditions... but what I do know is that particular number is 0xFFFFFFFF.FFFFFFFF if interpreted as single-precision floating point.[edit: or -1 if interpreted as a signed integer. n00b mistake. :)]

Anyway, aside from trying to blow up the CPU and destroy the Universe, samples of the ADC and DDS are on their way, the DBMs haven't turned up after nearly a month, ST are dragging their feet a little on bare chips (not that I'm complaining, they're worth the wait) and board progress is slow.

All in good time!

Friday, 27 June 2014

Getting started with ChibiOS

So after struggling along for a while with OpenCM3, I decided "screw it" and see what the whole ChibiOS/RT thing was about.

See, was under the impression that it was an actual OS; but in fact it is no more than OpenCM3 is. ChibiOS is pretty much a library that turns your microcontroller code into the OS itself, and so far, I'm pretty impressed.

Getting started is simply a matter of copying the appropriate directory from demos/ within the ChibiOS installation, and rewriting main.c from scratch.

What initially attracted me to ChibiOS was its rather impressive HAL which supports pretty much every AVR, ARM6, ARM7 and high-end PIC out there. Even the STM32F4xx historically troublesome USB device PHY is implemented.

It transpires, the SDR box I am currently designing is well within possibility, at least from a software standpoint. All of the current controller ICs that we are planning on using have been in some way or another:
  • the Analog Devices AD7356 analog-to-digital converter
  • the TI/NatSemi D83848C 10/100Mbit Ethernet PHY
  • pretty much any SDRAM chip we care to use via FMC
USB Serial (over CDC ACM) is a breeze as well since a lot of the work is done for you, and a lot of the scary endpoint & pipe configuration is done for you.

In a previous post I went over setting up plain TTL serial via the Arduino; this is all it takes in COS:


#include "ch.h"
#include "hal.h"
#include "chprintf.h"
 
int main(void)
{
        //Initialise the HAL.
        halInit();
        //Start up ChibiOS/RT's threads, scheduling, etc.
        chSysInit(); 
 
        //`pal' is the Pad Abstraction Layer.
        //Very similar syntax to our old OpenCM3 example: 
        palSetPadMode(GPIOA, 2, PAL_MODE_ALTERNATE(7));
 palSetPadMode(GPIOA, 3, PAL_MODE_ALTERNATE(7)); 
 
        //Notice the lack of settings! Passing NULL uses a set of sensible
        //defaults: 38400 8-N-1
        //Everything is still configurable. Simple, but powerful!
        sdStart(&SD2, NULL);

        //And a built in printf. No need to mess with Newlib & syscalls!
 char str[] = "Hello from ChibiOS/RT 2.6.4\r\n";
   chprintf((BaseSequentialStream *)&SD2, str);

        //Timers are all dealt with behind the scenes, too.
 chThdSleepMilliseconds(500);
} 
 
 
I've yet to evaluate the performance hit taken, but from what I have read, it's not too bad. Context switches (thread-thread switching) seems to be the big performance killer, so I'll be making minimal use of threads.

It also integrates with lwip for a nice and simple TCP/IP stack.

Back to playing with USB serial it is for now, though.

Monday, 23 June 2014

Talking to the STM32F4 Discovery: Discovering the USART

So as promised, we're going to take a look today into the Universal Synchronous/Asynchronous Receiver/Transmitter, or USART, function of the STM32F4 chips, specifically the STM32F407 which is on the basic STM32F4 Discovery board.

In my last post I mentioned using the Arduino as a serial<>USB adapter. If you are interested in this kind of thing it's likely you have an Arduino lying around. If you don't, a simple TTL serial to USB adapter will suffice.

If you're going the Arduino route, hook up PA2 on the Discovery board to RX on the Arduino, and PA3 on the Discovery to TX on the Arduino. Then simply short RESET to GND, and then you can simply use a program like minicom (or it's Windows equivalent - Hyperterminal?) pointed at the Arduino serial port to send and receive data directly to the Discovery USART.

Here's a Fritzing sketch to show you the basic idea:


So now we have the hardware side dealt with, we can now turn our attention to programming the STM32 to talk over serial.

All peripherals on the STM32 have their own linkage into the RTCC system, including the USART, so we need to initialise the clock for the USART first. We're going to use the same structure as before with an Arduino-style setup()/loop() kinda deal.

On the Discovery board, USART1 conflicts with certain other peripherals so we are going to go with USART2 here, which is broken out directly to PA2 and PA3 on the board. (USART1 is PA9/PA10).

rcc_periph_clock_enable(RCC_USART2);

And as usual, set up the pin mode. USART is an alternative dedicated function of these GPIOs, so we must use gpio_set_af with the appropriate parameter. Looking at the datasheet , we can see that USART2 is on AF7:

So let's set the pins to AF mode, and point the muxer (that big trapezoid) at AF7 to activate the USART:

gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2 | GPIO3);
gpio_set_af(GPIOA, GPIO_AF7, GPIO2);
gpio_set_af(GPIOA, GPIO_AF7, GPIO3);

Now we get onto the USART specific stuff you will want to configure with any USART module, like baudrate, stopbits, polarity and parity. Here's how you would set 9600 8N1:

usart_set_baudrate(USART2, 9600);
usart_set_databits(USART2, 8);
usart_set_parity(USART2, USART_PARITY_NONE);
usart_set_stopbits(USART2, USART_STOPBITS_1);

Additionally, you will more often than not, want to turn off hardware flow control:

usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE);

And finally we decide in what direction we want data to flow. In this case we are both sending and receiving so we set USART_MODE_TX_RX:

usart_set_mode(USART2, USART_MODE_TX_RX);

Oh, nearly forgot: we need to then turn it on:

usart_enable(USART2);

To send bits over the USART from the Discovery, you simply need to call usart_send to send asynchronously (without waiting for the send to finish) or usart_send_blocking to send synchronously.

Which you will need to use will depend on your application but if like me you are dealing with high speed real time data then you will want to use the asynchronous version. However for most purposes the synchronous (blocking) version will suffice; bear in mind if you are using the async version you need to handle keeping bytes in order yourself or it will send all the characters at once and yuowliidne puwtihyuorc a  htracr,estoufo  derro keiltshi.

The preceding code all goes in your setup function, and in your loop function it is simply a matter of calling

usart_send_blocking(USART2, 'h');

You can only send one character at a time using usart_send, but to send a string it is simply a matter of iterating over each character in turn using a for loop.


There is a better way, though!

*: This requires libgcc/newlib which is compiled soft-float (argh!) in the binary ARM GCC toolchain. IF you want this to work, you will need to download and build the entire cross-toolchain youself making sure to specify a hard-float toolchain and ensuring libgcc/newlib are compiled with VFP instructions. Have fun with that, I'd rather just reimplement printf myself. :)

You can define a system call so that printf and friends from the standard IO libraries will work.* System calls start with an underscore, and the one we need to implement to make *printf work is _write, which has the prototype

int _write(int fd, char *ptr, int len)

All you have to do is implement the function, which is left as an exercise to the reader. Don't forget to #include <stdio.h> in order to pull in the definition. Here are some hints on writing the _write system call:
  • You already have the length of the array *ptr as len.
  • for(int i = 0; i < len; i++){ .. }
  • File descriptors (the first argument) can safely be ignored unless you're performing file IO.
  • _write returns the number of characters written to the output device. You can cheat and return len or i, but cheating is bad, mmkay?
Receiving is a bit more complicated. Because we don't want to sit blocking the CPU while we wait for data to come in, the best course of action is to have the USART trigger an interrupt so that portion of receive code only runs when data comes

First you need to set up interrupts in the setup function, and include the NVIC (Nested Vector Interrupt Controller) headers in addition to the ones you already have:

#include <libopencm3/cm3/nvic.h>

To set up interrupts on USART2, at the top of setup():

nvic_enable_irq(NVIC_USART2_IRQ);
gpio_set_output_options(GPIOA, GPIO_OTYPE_OD, GPIO_OSPEED_25MHZ, GPIO3);
usart_enable_rx_interrupt(USART2);

Then, similarly to how we do things on the AVR side we just need to implement an ISR (interrupt service routine) to handle the interrupt.

#define USART_RXRDY(u) \
        (((USART_CR1(u) & USART_CR1_RXNEIE) != 0) && \
         ((USART_SR(u) & USART_SR_RXNE) != 0))

void usart2_isr(void)
{
    if(USART_RXRDY(USART2))
    {
        char data = 'a';
        data = usart_recv(USART2);
        _write(1, "RX = ", 5);
        _write(1, &data, 1);
        _write(1, "\n", 1);
    }
}

You can simply stuff each character in an array as it comes in if you need to read a string.

[Working USART example]

I guess this just about covers everything, I hope this has helped someone. Next up I'll be covering interfacing with an ADC (analog to digital converter). I know the STM32 has an internal ADC; but if you look at the list of my ADC requirements:
  • Greater than 4MSPS sample rate
  • 10 or more bits resolution
  • very low noise
  • available in small quantities/samples
  • user replaceable
the STM32's ADC meets only two of those requirements. In the end it is looking like I am going with the Analog Devices AD7356 which meets all of the above, and is only £5 in quantity to boot.

Might be a while though, since I have to actually get the part, break it out to a board, and get to grips with SPI on this thing.

Until next time!