Skip to main content

STM32 Pushbutton Example: Part 1. Software Timer

Project Design

Pushbuttons, or tactile switches are convenient way of interacting with the firmware, whether the final product retains them as its UI components or not. There are a couple of things to consider to implement the pushbutton handling features.

Debouncing and Software Timer

First you have to deal with the contact bouncing effect of the switch.  Following snapshot of  an oscilloscope from Wikipedia page illustrates one situation. However it can be radically different from one case to another.


There are numerous ways of addressing the issue, each of which has its own pros and cons. So one method works better in one situation but not necessarily in other cases primarily depending on the chattering characteristics but also depending on other situations. Here we are going to use software timer, in which a timer routine checks (samples) the state of the switch with a regular interval.

Short Click vs. Long Click, Single Click vs. Multiple Click

Another consideration has regard to endowing multiple functions to a single button. So you want to distinguish a short click from a long click, or a single click from a multiple click. This requires an algorithm that keeps track of the state of the button in time. So 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 potential click or it can declare the single click event now. The timer based pushbutton check routine is well suited for this purpose. It can implement this function with minimal complexity.

Event Queue 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.

Typical interrupt service routines including software timer routines fall into this framework. Imagine that you want to send a UART message when certain button is clicked. It is not a good idea to put a UART Tx function inside a GPIO external interrupt routine that detects the rise or the falling edge of the button signal, especially when the UART Tx function is a blocking call.

Instead your interrupt service routine only sets a flag, or posts an event. Then the main loop checks the flag later and processes it asynchronously. An event queue as a form of FIFO ring buffer is used for this purpose.

UsrTimer

UsrTimer is a function that implements a software based timer. The base housekeeping routine, UsrTimer_Routine() should be run at a regular interval, which dictates the resolution of the timer. Thus if you call this function inside the SysTick callback then the resolution, and the basic unit of the timer becomes 1 millisecond. Source file
  290 /** SysTick callback function override.
  291  */
  292 void HAL_SYSTICK_Callback()
  293 {
  294     // UsrTimer_Routine will have 1msec resolution
  295     UsrTimer_Routine();
  296 }
A new software timer routine can be registered by calling UsrTimer_Set() function. Source file
  105     // start a timer routine: 100msec period, perpetual
  106     result = UsrTimer_Set(100, 0, TestCallback);
In this example, the function TestCallback() will be executed for every 100msec. Note that the function returns the id of the timer. You can set the timer as perpetual or you can set it to be run for a limited number of times. So for example, if the TestCallback() is toggling a LED, you can see that it will change on/off state at every 100msec. Source file
  321 void TestCallback()
  322 {
  323     HAL_GPIO_TogglePin(TEST_LED_GPIO_Port, TEST_LED_Pin);
  324 }
You can also pause/resume or stop the timer at any moment. Source file
   45 /// Initialize all timers
   46 void UsrTimer_Init();
   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>>

Comments

Popular posts from this blog

A Simple STM32 Example Project

Most of the embedded projects share certain initial steps. You need to confirm the clock settings before doing anything, then you want to have debug connection via a UART channel since it is cheap  but still it can provide useful information for debugging. Let us start with CubeMX. You select a device/board of your choice, set up the pinouts for one GPIO output and one UART port. Configure the clock if necessary then create a project. Clock Checking using SysTick The sanity of the clock setting can be done by checking the SysTick interval. All Cortex-M series core have SysTick timer by default, which should fire at 1msec interval while the MCU is active. In the STM32Cube, the SysTick is initialized by HAL_Init() call, which in turn calls SysTick_Config() in CMSIS. Once the SysTick is initialized, it generates 1 msec interrupt and this interrupt is handled by SysTick_Handler() according to the Cube framework. Source file 1 /** 2 * @brief This function handles

STM32 USB MSC Device with FLASH memory

USB Mass Storage Class framework implements bulk-only-transfer (BOT) with SCSI protocol. USB packets from the host eventually converted SCSI transport commands by the middleware, in which data is exchanged (read / write) in 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 the file usbd_storage_if.c. In this case, 8Mbit Flash memory was used. During initial enumeration, this information is registered to the host. The middleware maintains one logical block size of buffer and handles USB transaction where each payload is only 64 bytes. It then calls SCSI requests to store / retrieve data to / from physical

STM32 USB MSD with SD Card

Build a low level driver for SD card, then the glue logic for FatFs and USB MSD is pretty much the same as Flash memory case posed before. In case of SD card, sector size is 512 in most of the cases. Thus the memory requirement is much relaxed. You can even allocate a file buffer that is bigger than the sector size. FatFs site has a  dedicated page for MMC/SDC, on which you can find fairly detailed explanation about how to interface MMC/SDC via SPI bus. Implementation should be straightforward until you encounter with cheap SD cards that do not behave very well. In such cases, you either have to protect your code with redundancy or just stick with quality devices. If you choose SanDisk or Kingston brand, you will be safe. ADATA on the other hand, frequently generates timeout error at first try. Most of the SD card sockets have a pin to detect the presence of the card. This pin is usually connected to GND pin or some other pin. You can use this to generate interrupt whenever a ca