Portoscope Part 3: Firmware
Firmware Entire firmware for the MCU was written in C language and I used atollic trueStudio toolchain for the development. It’s an Eclipse (which is always a plus) based environment with GNU C compiler and atollic’s ST-link GDB server for debugging. Standard 20-pin JTAG port was used for programming/debugging purposes. The MCU also provides trace support for debugging but I did not bother to use it. Now obviously a comprehensive description of the entire firmware is out of scope of this article. I will however describe the code structure and some highlights of the firmware.
Figure 8: MCU connections The source code is divided into four main files: main.c, LCD.c, touch.c and UI.c. The very first thing which I had to figure out was to read out the samples from the FIFO memory. The FIFO structure is basic and if you provide a read strobe signal to the IC it will spit out the contents which can be read via the MCU’s GPIO. For the strobe signal I used a GPIO which gets toggled between logic low and logic high inside a while loop in main.c. Inside the loop the sample are stored in 2 different integer arrays for each channel. If an empty memory flag has been detected the loop is broken and the sample arrays are sent for signal processing. Simple enough.
Figure 9: Code snippet from main.c
Apart from acquiring samples main.c also contains functions to control internal and external peripherals like the programmable clock (SPI), gain-select multiplexers, LCD, touch panel and the DAC. And some initialization functions to configure the MCU clock settings, GPIO settings etc.
Not going much into further detail let’s have a look at the main loop which runs forever inside the MCU. AcquireWave is the function which I just discussed above, it gets all the samples from the FIFO and stores them into 2 arrays. TP_GetDisplayPoint is used to get the touch point co-ordinates from the touch panel. This function is defined in touch.c and returns an instance of a structure co-ordinate Coordinate which basically has two data members an unsigned integer for X co-ordinate and one for Y. Then function UI_ReadControls is called which checks if there is a touch event such as button press. The function sets the global flag for that particular event and UI_UpdateControls refreshes the screen and responds to the touch event such as opens a menu.
UIDrawWave is used to display the waveform on the screen. All the functions with prefix UI are present in UI.c file including this one. To keep the screen refresh rate high I have used double buffering in which the previous waveform is kept, and when the new one arrives instead of clearing the entire screen only the previous waveform pixels are erased and new ones are drawn. UI_DrawMeasureBar draws a bar at the bottom which displays all the signal measurements like peak-to-peak voltage etc. These measurement formats can be changed by the user and available options are: mean voltage, peak-to-peak voltage, RMS voltage; for frequency available formats are: time period, frequency. Functions CH1DC and CH2DC control DAC outputs of channel 1 and channel 2 respectively. Similarly CH1Gain and CH2Gain control the gain sensitivity by selecting respective feedback resistors for the vertical amplifier.
I know it was a quick overview of the firmware but like I said a detailed explanation of the firmware could be a different article on its own. Although I would not mind if you want to see the complete source, just email me at firstname.lastname@example.org with a reason as to why you need the code and a promise that you will not use it without proper reference to the author (which is me). I will however explain the triggering mechanism of Portoscope. Triggering is of two types hardware and software, like you must have guessed hardware triggering is done in hardware using comparators to detect the trigger level. Software triggering is different and can be performed in more than one ways.
The main advantage of hardware is that it almost happens in real-time, as soon as the comparator detects trigger level it will assert the output and a CPLD, FPGA or any other programmable logic chip could be used to enable the write operation. The disadvantage is that as writing samples starts after the trigger level is detected you cannot see what happened before that.
To record pre-trigger samples something called a ring-buffer is used, which is basically a linear memory but the data is stored in a circular manner. So after the memory is full it does not clear the entire memory just places the pointer back to the start of memory and writes new data on top of previous one. The write process starts irrespective of trigger event and a pointer is set where the trigger point is detected. So when the samples are read from the buffer, it can either start from the trigger pointer before the pointer for pre-trigger information. Check this instructable for more information on ring buffers:
In software triggering the data is recorded irrespective of trigger point into a buffer. Then the buffer is scanned for trigger level, and as the samples before the trigger points are also stored pre-triggering can be easily performed. The only downside is that software triggering does not happen in real time as first the data is stored in a memory and then processed by the controller.
So that was all about the MCU and firmware, wait I did not mention about the clock source. All the STM32 chips have an inbuilt oscillator but you can opt for an external one too. I went with an external crystal because I think it is recommended as most of MCU boards have an external one. Maybe it provides lower jitter than the internal crystal but I am not entirely sure of this. So anyways an external crystal oscillating at 8MHz and the MCU has a PLL which allows you to multiply the frequency up to 72MHz.
Powering the device The main power source of the device is a battery as I wanted the device to be portable. It’s a 3.7V 1050mAh Nikon camera battery. It’s important that you have a clean power source with minimum variations for that I am using a 3.3V LDO (low drop-out) regulator. This is basically a linear voltage regulator but allows a very low drop-out for e.g. my regulator has a dropout voltage of 260mV. This means that if the battery’s supply voltage is greater than 3.3 + 0.26V the regulator will provide a stable 3.3V power line; the output will begin to decrease once the battery voltage falls below 3.56V. As the battery is rechargeable li-ion, I thought it would be nice if could add a battery charger on the board which charges the battery through USB. I used MAX1555 as this was the IC which sparkfun used for their LiPoly charger board . The charging circuit (figure 10) is pretty straight forward with a 5V USB input on one side and battery connection on the other, and a few decoupling capacitors to stabilize the voltages. Unfortunately the charging system didn’t quite work, as in the battery would not get charged even if I leave it for 6 hours. I believe the reason is that I did not add any on-off switch to my Portoscope (I know that was stupid) so basically if the battery is in the device is on at all times. And the current was constantly being drawn from the battery which was hindering the charging process. Other than that I could not think of a problem and I had no way to solve it either so I just used my camera’s charger to charge the battery.
Figure 10: Power supply circuit