Daisy-chained 74HC595s controlled by SPI on STM32F4 – part 2 : code

[part 1 part 2]

In this post the code I will present the code related to the simple project described in my previous post.

Basically this piece of code send some data through one of the SPI using DMA. This approach has two nice properties: the CPU does not have to wait until the transfer has finished; moreover, if the data at the 595s’ outputs need to be maintained for a certain period of time (e.g.: in order to communicate with some old slower logic) one can use the circular mode of the DMA to manage the timing, again without afflicting CPU performance.

So, first we want to configure the STM32 peripherals for our purpose. For this purpose, using the nice STM32CubeMX from STMicroelectronics is seems perfect.

The STM32CubeMX project can be downloaded from here, but mainly we:

  • configure clocking (the external crystal is 8 MHz, this involves the RCC configuration)
  • configure some GPIOs (the 4 LEDs and one line to control the RCLK input of the 595s)
  • configure the SPI
  • configure the DMA to use the SPI
  • configure the SWD (Serial Wire Debugging)
  • configure the USART (for debug)

The complete code, that can be downloaded from here, mainly performs the following tasks:

  • Perform some initialization (mainly generated by the STM32CubeMX utility)
  • Enter the main cycle in which:
    • Prepare the data to be sent to the 595s
    • Move the RCLK pin on PC3 (the setSTCP function)
    • Start the DMA transfer
    • Collaterally some LEDs on the discovery board are toggled and the time required by the DMA to perform the transfer is measured and sent through the serial port.

Of course, take a deep look at the code to understand what it exactly does before executing it on your device.

 

Happy coding 🙂

 

 

Advertisements

Daisy-chained 74HC595s controlled by SPI on STM32F4 – part 1 : working principle

[part 1 part 2]

In this post I will describe a simple project. The idea is to use a bunch of daisy-chained serial-in/parallel-out shift registers (namely the 74HC595) to expand my STM32F4Discovery board.

First questions: the discovery board has a plenty of GPIOs, so why not using them directly? In other words: why using external circuitry to obtain something that can in principle be obtained simply using the GPIO configured as outputs?

Some reasons:

  • With only 3 wires on the controller side we can control multiple outputs; this simplifies circuit routing;
  • the number of outputs can be increased simply by increasing the daisy-chain length (at the cost of slower update speed);
  • using the SPI in DMA mode, the CPU is free to perform other tasks while the external outputs are updated; this can become a substantial performance improvement if the number of lines we want to control is greater than 16 (the size of the GPIOs per register);
  • the ‘595 can produce 5V signals.

Of course this approach has also some cons:

  • More devices are needed, one ‘595 per 8 lines, this has a cost and requires space on the PCB (but more signals do, too);
  • more devices have to be powered;
  • the update speed of the output lines is limited by the speed of the SPI channel and the ‘595 propagation time; and the more the output lines we want to control, the slower the maximum update speed.

The reference circuit that I’m considering contains four 74hc595 registers, allowing to control 32 output lines:

stm32_74hc595_schematics

Of course, for practical applications the output of the ‘595s can be routed to a suitable connector instead of the LEDs.

If 3-state outputs are needed, the G input of the ‘595s can be controlled by (separately per chip or all together with a single line); in our case the ‘595s outputs are always active.

The 74HC595 contains two main items: one 8-bit shift register and an 8-lines latch. Simplifying, the working principle of the ‘595 is simple (pin names reported on datasheet from various manufacturers may vary, so I’ll refer to the names reported in the previous figure):

  • At the rising edge of the clock input SRCLK, data present at SER input is loaded into the shift register;
  • when a rising edge is detected at the RCLK, the data contained in the shift register are presented at the outputs QA..QH;
  • ‘595s can be daisy-chained thanks to the shift register output, at /QH;
  • a shift register clear input SRCLR is available, as well as an output enable input G.

The timing diagram of the 74HC595 is reported in the following figure, in which a single pulse is propagated in the shift register and presented at the output pins:

74hc595_timing

So, the basic (and far from being novel) idea is to write four bytes to one of the SPIs of the STM32F407 microcontroller of the discovery board to update the 32 output pins in a single operation using the DMA.

In the next post, the code for the STM32F4 microcontroller will be described.

Using SCons with GNU toolchain for ARM on Windows – part 3 : the SCons script

[part 1 part 2 part 3]

So we have prepared the code that we want to compile using SCons.

1. Command prompt

Since SCons will be invoked from the command line, it’s nice to have a batch file that will open the prompt with the necessary settings; so let’s create the following file in the project folder and call it shell.bat:

REM Add Python and tool-chain locations to system path
set PYTHON_PATH=C:\Python27
set GNUARM_PATH=D:\APPLICATIONS\_DEVELOPMENT\_LANG\GNUARM\GNU Tools ARM Embedded\4.9 2014q4\bin
set OPENOCD_PATH=D:\APPLICATIONS\_DEVELOPMENT\_LANG\GNUARM\OpenOCD\openocd-0.8.0\bin-x64

set PATH=%PYTHON_PATH%;%GNUARM_PATH%;%OPENOCD_PATH%;%PATH%

start cmd.exe

Of course the specified paths must be substituted with correct ones. Now, if we double-click on the shell.bat file, a new command prompt will be opened.

2. The SCons script

Let’s then create a file in the project folder and call it SConstruct with the following contents:

# gnu arm toolchain must be already in system path

import os
env = Environment(ENV = os.environ)

env['AR'] = 'arm-none-eabi-ar'
env['AS'] = 'arm-none-eabi-as'
env['CC'] = 'arm-none-eabi-gcc'
env['CXX'] = 'arm-none-eabi-g++'
env['LINK'] = 'arm-none-eabi-g++'                # predefined is 'arm-none-eabi-gcc'
env['RANLIB'] = 'arm-none-eabi-ranlib'
env['OBJCOPY'] = 'arm-none-eabi-objcopy'
env['PROGSUFFIX'] = '.elf'

# include locations
env['CPPPATH'] = [
    '#Inc',
    '#Drivers/CMSIS/Include',
    '#Drivers/CMSIS/Device/ST/STM32F4xx/Include',
    '#Drivers/STM32F4xx_HAL_Driver/Inc',
    '#Drivers/STM32F4xx_HAL_Driver/Inc/Legacy',
    ]

# compiler flags
env.Append(CCFLAGS = [
    '-mcpu=cortex-m4',
    '-mthumb',
    '-O2',
    '-fsigned-char',
    '-ffunction-sections',
    '-fdata-sections',
    '-std=gnu11',
    '-fmessage-length=0',
    '-mthumb-interwork',
    ])

# linker flags
env.Append(LINKFLAGS = [
    '-ffunction-sections',
    '-fdata-sections',
    '-TTrueSTUDIO/Discovery001 Configuration/STM32F407VG_FLASH.ld',
    '-Xlinker',
    '--gc-sections',
    '--specs=nano.specs',
    ]) 

# defines
env.Append(CPPDEFINES = [
    'STM32F407xx',
])

# build everything
prg = env.Program(
    target = 'main',
    source = [
        'Src/main.c',
        'Src/stm32f4xx_hal_msp.c',
        'Src/stm32f4xx_it.c',
        'Src/sys/startup_stm32f4xx_fromCoocox.c',
        'Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c',
        'Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c',
        'Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c',
        'Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c',
        'Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma_ex.c',
        'Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash.c',
        'Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ex.c',
        'Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_flash_ramfunc.c',
        'Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c',
        'Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr.c',
        'Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pwr_ex.c',
        'Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c',
        'Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc_ex.c',
        'Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c',
    ]
)

# binary file builder
def arm_generator(source, target, env, for_signature):
    return '$OBJCOPY -O binary %s %s'%(source[0], target[0])
env.Append(BUILDERS = {
    'Objcopy': Builder(
        generator=arm_generator,
        suffix='.bin',
        src_suffix='.elf'
    )
})

env.Objcopy(prg)

This is our SCons configuration file. SCons experts will deservedly turn their nose up, because it’s super-simplified. SCons can do a lot more and better, but this script works and proves how it can be used to cross-compile for our target.

What the script does is very simple:

  • In the first section it set the parameters that inform SCons about the toolchain we are using.
  • Then the include libraries are added, together with compiler and linker flags.
  • Finally all the sources are compiled and the final program is built. Afterwards the produced .elf file is converted in the binary form suitable for uploading to flash.

3. Building and uploading the project

In order to build the project, open a new command prompt by double cliccking on the previously created shell.bat file and type scons.

If everything works, at the end you should read something like “scons: done building targets.” and a couple of files should now be present in the project folder: main.elf and main.bin.

The second one can be uploaded to the MCU flash using the command:

openocd -f board/stm32f4discovery.cfg -c "init; reset halt; flash write_image erase main.bin 0x08000000; reset run; shutdown"

After some seconds, the board should start blinking, and if you connect to the USART2 port some chars are received.

Happy coding!

Using SCons with GNU toolchain for ARM on Windows – part 1 : necessary tools

[part 1 part 2 part 3]

SCons is a software construction tool, a sort of alternative to the ubiquitous GNU make. What makes SCons very interesting in my opinion is that its configuration files are actually python scripts.
In this very brief – and probably imprecise – post I just want to jot down a way to use SCons with the GNU toolchain for ARM, under Windows 7.
What I’m going to do is produce the project infrastructure with the nice STM32CubeMX application from STMicroelectronics and compile it with SCons.
So, let’s start from the beginning.

1. Install GNU toolchain for ARM

Download and install GNU toolchain for ARM. Download it from launchpad.net; in my case I downloaded and installed “gcc-arm-none-eabi-4_9-2014q4-20141203-win32.exe“.
During installation, it’s better NOT to add the toolchain path to the environment, as this will be managed in a suitable batch file in order to keep the system environment clean.

2. Install OpenOCD

Download and install OpenOCD from sourceforge.net; in my case I downloaded and installed “openocd-0.8.0.7z“. Here, “installing” means extracting the content of the downloaded archive somewhere.

3. Install the cable drivers

Now I guess it’s a matter of which h/w is used. In my case I’m using an STM32F4Discovery board, which embeds an ST-LINK/V2 cable, and I’m working under Windows 7 64 bit, so I downloaded the STSW-LINK003 drivers from STMicroelectronics website.
After installation, the STM32F4Discovery board is recognized as a “STMicroelectronics STLink dongle” peripheral, under the “Universal Serial Bus devices” category in the device manager.

4. Install the Python language

I’m working under Windows 7, which does not include “native” support for python, so it must be installed. SCons requires version ❤ of python, so let’s download and install python 2.7.x from the official website.

5. Install SCons

Now we can download and install SCons. Download the archive from SCons website; in my case I downloaded the source version 2.3.4 and followed the instructions on the SCons manual (python setup.py install).

In the next posts the project will be generated and a SCons script will be produced.