Hardware Design Overview

This articles will explanation development process of hardware design.

In hardware design, the work involve includes:
  • Final product specifications
  • Components selections: MCU, sensors, non-volatile memory, slave IC
  • Schematic design
  • PCB design
  • PCB prototype fabrications
  • Components purchase
  • Prototype testing and verifications

Firmware Design Tricks And Techniques


This is the place holder for firmware design tricks
 
To be continue...

Tricks 1

Explanations of tricks 1

Tricks 2 

Explanations of tricks 2

Lesson 8: Part 1: PWM - LED Brightness Control

Introduction

PWM - namely Pulse-width modulation, enable us to output digital signal that toggle between high(3.3V) and low(0V) state in desire frequency. A example of signal will be as below:
Example of PWM waveform


3 terms that is use in PWM are below:
  1. Period : the time require to complete a cycle of high state and low state.
  2. Frequency: Inverse of period.  The frequency of the PWM waveform.
  3. Duty cycle: A measurement of the ratio of high state as compare to a period of PWM. A 50% of duty cycle meaning half of the period is at high state with another half of period at low state. Example above have a 33% duty cycle.
By applying capacitor at PWM pin, we can 'smoothen' the PWM and use it as DAC. For example using it to control the brightness of the LED. Increase LED brightness by increase duty cycle and vice versa.

Another important usage of PWM is in servo motor control. 

Example Code

In STM32F030, the PWM is generate using one of the MCU timer.

In the coming example, we will be using Timer14 and enable the PWM waveform output at pin PB1. And by connecting the PB1 into on-board LED-LD3, we can observe the PWM effect without any equipment.

As on board LED is at pin PC9, we need to first pull up the PC9 pin, and then physically connect the pin from PC9 to PB1 as shown below:
Short Between PC9 (LED-LD3) and PB1 (PWM output)


You can retrieve the sample code at github here.


In pwm.c source, there is additional define namely: PWM_PB1_BLINK. This define is use to demonstrate a PWM waveform with different frequency, thus enabling the LED blink at different rate.

When enable PWM_PB1_BLINK, LD3 will be blinking at rate of 0.5seconds, due to PWM generated have period of 0.5seconds.

When disable PWM_PB1_BLINK, the brightness of the LD3 will be reduce slightly. This is because the PWM is running at 50% duty cycle, thus reducing the overall brightness. But no blinking is observe because the PWM waveform is running at 10millisconds (refer below for the capture waveform on scope), which is too fast for our eyes to perceive. One exercise is you can try is change the value of PWM_DUTY_CYCLE. When PWM_DUTY_CYCLE is reduce, the brightness will be reduce.
Disable define PWM_PB1_BLINK Generate PWM Of 10mS Period

SFR Configurations

Key configurations of SFR is describes as below:
  1. Enable Timer 14 block
  2. Setting of PWM duty cycle
  3. Settings of PWM period or frequency
  4. Configurations of Timer 14
  5. Configurations of GPIO-PB1
  6. Configurations of GPIO-PC9

Lesson 7: Part 2: ADC - Reading Sensor Voltage Input

Previously we work on the ADC function in reading on chip temperature sensor. In this tutorial we will be working on external sensor.
 
To simplify the work require, we will be using a potentiometer to simulate the change of resistor. A sample of potentiometer is this. By applying the potentiometer in a voltage divider circuit, we will be getting an output voltage that change when potentiometer resistor is changing.

Tutorial Setup

This tutorial will be reading performing ADC read at channel ADC_IN0 every seconds (using systick), then output it through USART interface(USART) into laptop hyperterminal.

Below shown the setup require on ADC section


Using Vin=5V,  R1=6.8K Ohm, potentiometer at R2 (with resisitance range of 0Ohm ~ 10K Ohm). The voltage output sample is shown in below:

R2 Resistance              Vout
0 Ohm                                 0V
1K Ohm                         0.64V
2K Ohm                         1.13V
5K Ohm                         2.11V
10K Ohm                       2.98V


The Vout will be connect into PA0, correspond to ADC_IN0 pin.

Actual Setup

The MCU is actually running at 3V, the ADC will give maximum reading of 0xFFF when input voltage is 3V.


Source Code Descriptions

SFR Settings Descriptions

The following SFR setup is require to enable ADC_IN0

A) ADC function

Power up initialisation
  1.  Enable GPIO-A block
  2. Change PA0 pin function to alternate function (ADC function)
  3. Enable ADC block
  4. Perform ADC calibration and wait for completion
  5. PowerUp ADC block and wait for completion
During Run time on Reading ADC result
  1. Select ADC channel (in our case it would be ADC_IN0:Channel 0)
  2. Enable single conversion mode/
  3. Select conversion resolution (in our case we use 12 bits)
  4. Start ADC process and wait for completion
  5. Read the result
B) USART
  1. Enable GPIO-A block
  2. Change PA9, PA10 pin function to alternate function( USART)
  3. Enable USART block
  4. USART settings on the following: 8 databit, 115200 baud rate, 1 stop bit
  5. USART transmit/receive enable
  6. Enable USART interrrupt receive
  7. Enable USART
  8. * For more detail on USART, refer to tutorial here.
C) SysTick
  1.  Set SysTick timer reload value
  2. Clear SysTick timer current value to 0
  3. Select SysTick timer clock source
  4. Enable timeout interrupt, SysTick timer
  5. *For more detail on SysTick, refer to this tutorial.

Tutorial Source Code

The tutorial source code can get clone from here.
I have purposely combine the previous tutorial (USART, SysTick) to show how a source code can be 'expand' when more features is being added. Now each MCU peripheral will have its respective source file (e.g. gpio.c, systick.c, uart.c)

Even though we are using potentionmeter to simulate the voltage change, a real life example work similar way. For example, a temperature sensor of LM35, is giving out a voltage change base on temperature. And the above code can be use with some tweak on the ADC result, to translate the result into temperature.

Lesson 7: Part 1: ADC - Reading On Chip Temperature Sensor

MCU is consider the processing unit of the embedded system. In order to allow MCU 'process', certain data need to be collect and feed into the system. For example, for a temperature monitoring system, it may require to pickup the current temperature from a temperature sensor. Or a burglar system will require to read current door contact sensor and feed into MCU. Base on these input then MCU will act accordingly.

There are may method the data (or sensor data) can be receive by MCU such as: SPI bus, IIC bus, ADC. We will be touching on SPI/IIC in future, for this lesson, our focus will be on ADC, which stands for 'Analog to Digital Converter'. As the name imply, the functionality of ADC is to convert analog signal(in this case is voltage) to binary data.

Take temperature sensor as example, when the temperature is changing, the resistance of the sensor will varies. Using this characteristic and apply the sensor into voltage divider circuit, we will have a voltage output that change according to the temperature. By using ADC, MCU can know the current voltage level, thus enabling system to know current temperature.

After reading the ADC section in Reference Manual (RM0360), there is actually an internal temperature sensor (Vsense). This would be an good example to demonstrate ADC function in reading temperature.

p.s:
Despite the idea of demostrating tempeature reading, it appears that much more work is require in order to perform a proper temperature reading, as described in RM0360: Section 12.9. Thus, the example would only demonstrate a ADC value change (increase) correspond to temperature increase.

Controlling ADC

Key function to enabling ADC is summarizes as below:
  1. Enable ADC peripheral
  2. Configurations on ADC
  3. ADC channel selection
  4. Start ADC process
  5. Wait for ADC process complete, then read the result
The source code can get obtain from here.


Lesson 6: Part 3: USART - Transmit Using DMA

In previous USART example, both transmit and receive will requie significant MCU invovlment in moving data in and out the buffer. This is (receiving example) what we usually perform in old days.

But now with a DMA(Direct Memory Access), a lot of processing can be offload to hardware instead. Using DMA, automated the byte transfer between USART buffer to RAM.

This is what we will be doing in this tutorial, we will be sending data out(TX) using DMA.

The sample code will read character being received and retranmist received characters back to sender using DMA. The receiving section remain as previous example, using interrupt and MCU to read the bytes from USART receiving buffer and place into dedicated variable.

Please take note this example only use DMA for TX(transmit), and not RX(receive).

Source Code Explaination

The sample code can get obtain from here.

 Code Initialisation

During code initialisation, below peripheral has to be initialised.
  1. GPIOA - Enable USART Tx/Rx pin and use as USART function
  2. USART - Enable USART block and related USART settings (e.g. baud rate, stop bits)
  3. DMA - Enable DMA function in USART transmission
  4. At the end of initilisation code , a message " Hello World" will be transmit out.
One changes on USART SFR is to enable a DMA transmit mode as shown in below:
USART1->CR3 |= USART_CR3_DMAT;

Some of the key settings in DMA includes:
  1. Source address: when the data come from
  2. Destination address: when the data should transfer to
  3. Transfer bytes: number of data to transfer
  4. Data size: Data size for each transfer : 8/16/32bits
  5. Transfer type: Peripheral to memory or vice versa, memory to memory
  6. Interrupt type: Half transfer or complete transfer

 Main Loop

In the main loop, MCU will continue check if there is any data being received. If data has been recevied, then it will read the received data and perform data transmission using DMA.

The configuration of DMA block is perform in USART1_Tx().

To ensure next DMA sending is only start when last sending is complete, a variable TXBuffers.Complete is using. Before start of transmission, this flag will be turn to 0. Upon complete of data sending, an DMA interrupt will be generated, and a call back function will be called to turn this flag into 1. Before data transmission, this flag will be check and system will enter a while loop if this flag has value 0 (meaning previous transmission is still in progress).

**This may cause a problem if previous transmission has large number of data to be send while system waited and eventually timeout and discard current data. To solve this, change in such a way reading on received data only happen when last DMA send is complete.

Lastly, I found a similar tutorial on here. This tutorial is more detail with oscilloscope measurement to prove the advantage of using DMA.

Lesson 6: Part 2: USART - Receiving Data From PC

In previous tutorial I am showing STM32F030 sending data and receive by PC. This tutorial will be working on the other way: PC sending data and receive by STM32F030. As USART is full duplex, while PC is sending data, STM32F030 also can send data simultaneously.

I have expand the code from previous tutorial to include the receiving section. And in this tutorial, we will be perform a character echo. Characters receive from PC will be transmit back to PC. Thus, we should expect character that we type in transmit section will be appear on receiving window of the software.

SFR Configurations

Refer to source code here for SFR configurations, all the related SFR on receiving is grouped in function name IntUSARTInit_RX in source file name int_usart.c.

Source Code Explanation

To avoid a blocking read that would occupied the whole MCU cycles, this example demonstrate the usage of interrupt (without DMA) on character receiving. 

When character is being received, interrupt routine ISR name USART1_IRQHandler will be called and this routine will read the character from buffer (USART1->RDR) and save into local buffer. Then the pointer PtrProd will be increment. The method use here is the producer/consumer model.

In the main program, the program will constant check if a data is available in buffer. When there is data available, a read from buffer will perform, then data read will be transmit out (using past tutorial).

Below is the video showing the code is running