USB MSC Device with FLASH Memory

USB Mass Storage Class implements bulk-only-transfer (BOT) with SCSI protocol. USB packets from the host eventually converted into SCSI transport commands by the middleware, in which data is exchanged (read / write) with the unit of logical block, typically 512 bytes.

This SCSI commands works well with SD card system where a dedicated controller does the job of managing the actual memory elements. If you want to use a FLASH chip as a memory unit instead, you need to handle read / write operation directly.

Fortunately, most of flash memory support 4KB block erase. This makes the 4096 bytes as a natural choice for the size of the logical block. In STM32Cube framework this is defined in the file usbd_storage_if.c

   94 #define STORAGE_LUN_NBR                  1
   95 #define STORAGE_BLK_NBR                  0x10000
   96 #define STORAGE_BLK_SIZ                  0x200

During initial enumeration, this information is registered to the host. The middleware maintains one logical block size of buffer and handles USB transaction whose each payload is only 64 bytes. It then calls SCSI requests to store / retrieve data to / from physical memory device in this case a FLASH memory when the buffer is filled. Thus the buffer size should be increased by the same amount in the file usbd_conf.h

  108 #define MSC_MEDIA_PACKET     4096

Now, remaing task is to simply convert SCSI block read/write commands into FLASH memory sector read/write operations.

    1 /**
    2   * @brief  .
    3   * @param  lun: .
    4   * @retval USBD_OK if all operations are OK else USBD_FAIL
    5   */
    6 int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
    7 {
    8   /* USER CODE BEGIN 6 */
    9     SFlash_ReadSector(blk_addr, buf);
   10     return (USBD_OK);
   11   /* USER CODE END 6 */
   12 }
   13 
   14 /**
   15   * @brief  .
   16   * @param  lun: .
   17   * @retval USBD_OK if all operations are OK else USBD_FAIL
   18   */
   19 int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
   20 {
   21   /* USER CODE BEGIN 7 */
   22     SFlash_WriteSector(blk_addr, buf);
   23     return (USBD_OK);
   24   /* USER CODE END 7 */
   25 }

Here the logical unit number(lun) is always zero. This is because there is only one LUN is defined. And the number of logical blocks (blk_len) is always one. This is because the internal buffer size is equal to the size of local block. See MSC_MEDIA_PACKET definition above.

When it is first plugged in, it appears as a raw disk with no FAT. So no logical drive will appear as if unformatted drive. It is necessary to create a FAT using fdisk. To minimize the overhead space, choose FAT16 <32M option in the fdisk (or cfdisk) utility.

Then format the drive

sudo mkfs.vfat -F 16 /dev/sdb1

At this point the host system may recognize the device as a formatted disk drive. Otherwise plug it out and in again. In the picture below, Nucleo32-L432KC (at the center) and Adesto AT25SF081 (bottom left) are used in this project. 

(source code)

STM32L052 Touch Sensor

Some of STM32 MCUs are equipped with TSC (Touch Sensing Controller), whose hardware performance is comparable to ASIC solutions. It is a self-sufficient module supporting multi-touch with decent SNR. And you can lower its power consumption down to few tens of micro amps.

Setting up the TSC requires to select one port for sampling for each group. One group consists of one sampling channel, where a sampling capacitor is connected, with one or more measurement channel, where a sensor pad is connected.

In each group, charge transferred from a measurement channel to the sampling channel automatically without firmware intervention. This is done independently from other groups. Thus channels belongs to each group are measured at the same time. In the same group, however measurement should be done sequentially among channels.

Following picture shows the test setup. A PCB touch panel on the right is connected to the STM32L052 breakout board on the left.

The MCU is programmed to measure the touch sensor value at every 100 msec. It transfers the measurement to a PC via UART connection. On the PC side, a python utility is prepared to collect data.

The python script also shows real-time plots as well as simple statistical data. From this data, you can easily check certain characteristics of the touch pad such as SNR, long-term drift, as well as the effect of the front cover structure on the sensitivity of the sensor.

It varies depending on the situation, minimum SNR of around 4 is considered to be necessary for the reliable operation. If the SNR is smaller than 3, additional post processing is required. If the noise is random, simple averaging can enhance the SNR effectively. Otherwise things get complicated. You may need to activate the spread spectrum (clock jittering) feature of the controller to make the noise spread out.

(source code)

Pushbutton Event Handler: Part 3

Event Queue

In the two step process described at the Part 1, short and fast process should transfer information of certain event to the long and slow process. This can be done simply by raising a flag. Alternatively it can be done more systematically by using a data structure such as a ring buffer.

You can create a ring buffer whose element size is few byte long and use it as a queue to convey short information about events. Let us call it an event queue. As a minimum, you need two functions, put and get (or enqueue and dequeue).

   66 #define EVT_QDEPTH              (8)
   67 /** The maximum size of the event data. It consists of one byte of event code
   68  * with variable length of data bytes.
   69  */
   70 #define EVT_QWIDTH              (16)
   71 
   72 /// Register a new event
   73 bool Evt_EnQueue(uint8_t *event);
   74 /// Checkout the oldest event
   75 bool Evt_DeQueue(uint8_t *event);
   76 /// Initialize the event queue
   77 void Evt_InitQueue(void);

As for the usage, for the sake of argument, let us the first byte of a queue element denote the category of event and the next byte detailed type. Then we can define event data structure for pushbutton handler like below.

    4 /** Pushbutton input event
    5  *
    6  * Event Data: (EVT_SRC)(EVT_TYPE)
    7  * 
    8  *  * EVT_SRC: id of the pushbutton that generated the event
    9  *  * EVT_TYPE: type of the event such as single click, double click, 
   10  */
   11 #define PBTN_INPUT              0x10        ///< event Source: pushbutton input
   12 
   13 #define PBTN_SCLK               0x01        ///< event Type: single click 
   14 #define PBTN_LCLK               0x02        ///< event Type: long click
   15 #define PBTN_DCLK               0x03        ///< event Type: double click
   16 #define PBTN_TCLK               0x04        ///< event Type: triple click
   17 #define PBTN_DOWN               0x05        ///< event Type: button state down
   18 #define PBTN_ENDN               0x06        ///< event Type: button state changed to up

When event is detected by the fast process (PushButton_Routine), it pushes relevant information to the queue.

  112         // up-down mode
  113         if(((pp.mode >> i) & 0x01)  == PUSHBTN_MODE_UDOWN)
  114         {
  115             // the button pressed
  116             if(((pp.new_state >> i) & 0x01) == 0x01)
  117             {
  118                 event[0] = EVT_PBTN_INPUT;
  119                 event[1] = (uint8_t)(i+1);
  120                 event[2] = PBTN_DOWN;
  121     
  122                 // post the event as long as the button is pressed down
  123                 Evt_EnQueue(event);
In the main loop, event handler (slow process) keeps checking the queue and processes it whenever new event is posted.
  115     while (1)
  116     {
  117         /* USER CODE END WHILE */
  118 
  119         /* USER CODE BEGIN 3 */
  120 
  121         // check event queue
  122         if(Evt_DeQueue(event))
  123         {
  124             switch(event[0])
  125             {
  126             // pushbutton event ================================================
  127             // event[1]: button id
  128             // event[2]: PBTN_SCLK, _DCLK, _TCLK, _LCLK, _DOWN, _ENDN
  129             case EVT_PBTN_INPUT:
  130 
  131                 if(event[2] == PBTN_SCLK)
  132                 {
  133                     UartPrintf("\r\nButton %d: single click.", event[1]);
  134                 }

(timing consideration) Since the software timer is based on 1 msec SysTick, each callback should do its job much faster than 1 msec, preferably within a few millisecond. So slow routines such as ADC conversion or SPI transaction should be strictly avoided.

(race condition) The event queue is shared by the slow processes and the fast processes, where the fast processes preempt the slow processes since they are the callback of SysTick interrupt. To avoid race condition, Software timer should be paused when slow process access the queue.

   63 bool Evt_DeQueue(uint8_t *event)
   64 {
   65     uint8_t i;
   66     bool flag = false;
   67 
   68     // disable all timers
   69     UsrTimer_Enable(false);
   70 
   71     // queue is not empty
   72     if(evt_queue.tail != evt_queue.head)
   73     {
   74         // copy event bytes into the buffer
   75         for(i = 0; i < EVT_QWIDTH; i++)
   76         {
   77             event[i] = evt_queue.buff[evt_queue.tail][i];
   78         }
   79         // move to the next position
   80         evt_queue.tail = ADVANCE_QPTR(evt_queue.tail);
   81         // set flag
   82         flag = true;
   83     }
   84 
   85     // enable all timers
   86     UsrTimer_Enable(true);
   87 
   88     // return with the flag
   89     return flag;
   90 }

(source code)

Pushbutton Event Handler: Part 2

Software Timer

In typical hardware timer use cases, you set up a timer with certain settings such as interval then assign a callback function that is called when the timer expires and let the function do certain task. You can instead write a timer callback function to call other callback functions, i.e. cascade the callback functions. This way, you can hook up multiple callback functions to a timer with arbitrary interval. Original timer now works as a base timer but the callback function works as a software timer.

Cortex-M processors have generic 1 millisecond timer called SysTick. Write a SysTick callback function that handles host of other callback functions with intervals of multiple of 1msec. For example, you can make the SysTick callback function call pushbutton state check routine every 300msec, as well as many other routines that are called at various intervals.

In STM32Cube framework, SysTick interrupt handler calls the function HAL_SYSTICK_IRQHandler (Warning: This was broken in the version 5.0.0 of STM32CubeMX. So you need to do it manually), which in turn calls HAL_SYSTICK_Callback function.

    1 /**
    2 * @brief This function handles System tick timer.
    3 */
    4 void SysTick_Handler(void)
    5 {
    6     /* USER CODE BEGIN SysTick_IRQn 0 */
    7 
    8     /* USER CODE END SysTick_IRQn 0 */
    9     HAL_IncTick();
   10     HAL_SYSTICK_IRQHandler();
   11     /* USER CODE BEGIN SysTick_IRQn 1 */
   12 
   13     /* USER CODE END SysTick_IRQn 1 */
   14 }

Thus it is a good place to put your software timer routine.

  355 /** SysTick callback function override.
  356  */
  357 void HAL_SYSTICK_Callback()
  358 {
  359     // UsrTimer_Routine will have 1msec resolution
  360     UsrTimer_Routine();
  361 }

Following example registers a function that toggles LED at every 100msec.

    1 void TestCallback()
    2 {
    3     HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    4 }
    5 
    6 main()
    7 {
    8     // start software timer routine
    9     UsrTimer_Init();
   10 
   11     // register a callbackfunction with 100msec interval
   12     UsrTimer_Set(100, 0, TestCallback);

Software timer implemented in the sample project file support following functions

   43 /// Initialize all timers
   44 void UsrTimer_Init();
   45 /// Enable or disable main routine
   46 void UsrTimer_Enable(bool flag);
   47 /// Clear the timer
   48 void UsrTimer_Clear(uint32_t index);
   49 /// Pause the timer 
   50 void UsrTimer_Pause(uint32_t index);
   51 /// Resume the timer
   52 void UsrTimer_Resume(uint32_t index);
   53 /// Main timer routine
   54 void UsrTimer_Routine(void);
   55 /// Set a new timer with the callback function
   56 int UsrTimer_Set(uint32_t interval, uint32_t duration, usrtimer_callback f);

(source code)

Pushbutton Event Handler: Part 1

Project Design

Pushbuttons or tactile switches are most popular input devices. But properly handling them is not so trivial. Let us check out what do we need to consider for the implementation.

(Chattering and Debouncing) Most type of mechanical contact switches produce contact bouncing effect. Following snapshot of an oscilloscope taken from Wikipedia illustrates this phenomenon well.

Actual situation varies a lot depending on the type of switch however. In a certain way chattering like above is quite exceptional, since it lasts only 2-3msec. Such short chattering can be dealt with simple hardware like R-C filter. More typically however chattering can last more than 100msec easily.

Robust implementation of pushbutton input handler thus requires polling of the switch state with a regular interval, eliminating erroneous input caused by momentary glitches.

(Short Click, Long Click and Multiple Click) Sometimes you want to assign a multiple functions on a single switch. You may want to distinguish between short click and long click or you may want to assign different tasks for single click, double click, triple click and so on.

This requires an algorithm that keeps track of the state of the button in time. For example when a single click is detected, it should be able to determine whether it has to wait certain amount of time for the next click or it can declare the single click event now. The timer based pushbutton state polling routine is well suited for this purpose.

(Event Generation and Handling) Finally, there is a situation when you want to divide one task into at least two parts, one part that needs to be executed in very short duration of time, usually much less than a millisecond, and the other part that has no particular time limit and that can be interrupted at any moment.

Instead of having a single routine that (1) polls states of pushbuttons regularly (2) determines the type of the button action based on the history, (3) then run certain tasks depending on the result of (2), you can divide them into two parts, i.e. fast processing part (1+2) and slow processing part (3). The fast processing part can be implemented as a interrupt callback function since it takes only a few microseconds at most. Then it generates an event when valid input is detected. The slow processing part can be called in a main loop checking the event, runs dedicated function if necessary, with no particular time constraint.

In the following posts, we will implement basic structure on a STM32 platform.

Data Plotting in wxPython

Using wx.lib.plot

wxPython has its own plotting library, which provides simple way of drawing large number of data on a canvas. It is convenient to use and it is fast. However you have only one axis per canvas and you can plot 2D graphs only.

To draw a line graph like above, create line objects using numpy

  214                 x = np.linspace(0,10,500)
  215                 y = np.sin(x)
  216 
  217                 # create lines
  218                 line1 = wxplot.PolyLine(list(zip(x, np.sin(x))),
  219                         colour='red', width=3, style=wx.PENSTYLE_DOT_DASH)
  220                 line2 = wxplot.PolyLine(list(zip(x, -np.sin(x))),
  221                         colour='blue', width=3, style=wx.PENSTYLE_LONG_DASH)

Then generate a graphics object and render it on the canvas. Here the canvas is implemented on a wxPanel. So you can embed it into any wx.Window object.

    1                # create a graphics
    2                 graphics = wxplot.PlotGraphics([line1, line2])
    3                 self.pnlPlot.Draw(graphics)

Using Matplotlib WXAgg backend

For more professional plot, you can use matplotlib, more specifically matplotlib WXAgg backend, where almost all the matplotlib features are available to wx.Python.  Thus you can create plots like below very easily.

The WXAgg Figure object and the FigureCanvas object are implemented on a wx.Panel as class members.

   40         # mpl figure object
   41         self.figure = Figure()
   42         # mpl canvas object
   43         self.canvas = FigureCanvas(self, -1, self.figure)

The shade plot on the left for example was generated by the code below.

  138                 # clear previous plot
  139                 self.pnlPlot.Clear()
  140                 # acquire new axes
  141                 ax1 = self.pnlPlot.AddSubPlot(121)
  142                 # we need figure object too
  143                 fig = self.pnlPlot.GetFigure()
  144 
  145                 # colormap
  146                 cmap = matplotlib.cm.copper
  147 
  148                 # import LightSource
  149                 from matplotlib.colors import LightSource
  150 
  151                 y,x = np.mgrid[-4:2:200j, -4:2:200j]
  152                 z = 10 * np.cos(x**2 + y**2)
  153                 ls = LightSource(315, 45)
  154 
  155                 rgb = ls.shade(z, cmap)
  156 
  157                 ax1.imshow(rgb, interpolation='bilinear')
  158                 im = ax1.imshow(z, cmap=cmap)
  159                 #im.remove()
  160                 #fig.colorbar(im)
  161                 ax1.set_title('shaded plot')

(source code)

wxPython Highlight Frontend

Highlight is a source code syntax highlighter that convert source code into HTML and other formats with syntax highlighting. It comes with a nice GUI as well as a command-line executable. This program implemented utilizes the command-line version of Highlight as its backend. It exposes only limited number of features of the original program. However you may find it convenient in certain situations.

The program has three main windows: one for the source code, one for the HTML view and one for the HTML code. In the HTML view you can check the result rendered by the HTML engine. In the HTML code page, you can see its actual HTML code. The HTML view window and the HTML code window are embedded in the notebook window as separate pages under the source code window.

The upper window (a text control) and the lower window (a notebook) are two client areas of a wx.SplitterWindow. So you can resize them by moving the sash in the middle.

On the right, you have several options to choose such as source syntax, output type, theme, font and size etc. Output result can be sent to the clipboard either in HTML text or in bitmap image. It can be saved to a disk file as well.

To enter a source, you can use a file dialog by clicking on the file name text control. The source window is a drop target for files. So you can drag and drop a source file to the source window. You can also copy and paste text from other programs, or you can just type in your code in the source window. If the source came from a file, the source syntax is automatically selected by identifying the file extension. Otherwise you have to select it manually.

The image generated by the program is done by capturing the screen shot of the HTML view window. So you can control the size of the image and the contents by moving the sash and the vertical scroll bar.

If you select certain area of the source code, then you will have the output that matches with the particular area.

In Windows, this image capturing works on the primary display but may not on the secondary display if your display setting uses different scaling factors on each display. (source code)

wxPython COM Terminal

PySerial provides a convenient way to handle data transfer over COM ports. It works on both Windows and Linux. This program is written using PySerial and wxPython to create a simple terminal emulator. It is pretty much similar to the example presented by PySerial itself.  However in this case, the UI is built on wx.Panel instead of wx.Frame. And actual reading of serial data is done in a separate thread.
All GUI components are populated on a single wx.Panel. On the left, a wx.TextCtrl is located as a terminal window. On the right, handful controls are added for for basic setup and control of the terminal window and the COM port. The controls on the right side can be hidden or shown at any time. This terminal supports ASCII mode and Hexadecimal mode. You can also save data to a disk file.

Since all the features are implemented on a wx.Panel, it can be embedded into any window including main frame window simply by creating an instance and passing a PySerial object(serial.Serial()). You can find an example on which two such panels are populated to implement a UART sniffer. (source code)

USB DUAL COM using PSoC 5LP

PSoC Creator provides a simple way to implement certain type of USB device including a USB composite device that has two CDCs. You can turn a $10 PSoC 5LP stick into a FT2232 like device or you can build a UART sniffer with few lines of code.

Dual UART in loopback connection

(Select a Component) Start a new PSoC project with the selection of appropriate PSoC device. Then pick a USBFS from the Cypress Component Catalog on the right.

(Import Descriptor) Open up the configuration dialog by double clicking on the component. At the Device Descriptor page, select the Import Descriptor Root (green down arrow) button. It will show up a file selection dialog box that allows you to import templates (xml files) from handful number of predefined settings. For the purpose of this post, choose the file USBUART_TwoComPorts.root.xml.

You will find that all the descriptors and items necessary for dual CDC are nicely populated. You can delete any existing device descriptors.

(Two UARTs) Finally populate two UART components and set their parameters such as baud, number of data bits, parity types to suit your purpose.

This component is initialized with the same way as any other USB component. However you need select one UART port before calling USB UART API for communication. (source)