Adding armbian supported boards to meta-sunxi Yocto (updated)

Intro

Yocto is the necessary evil. Well, this post is quite different from the others, because it’s more related with stuff I do for living, rather fun. Still, sometimes they can be fun, too; especially if you do something other than support new BSPs. Therefore, this post is not much about Yocto. If you don’t know what Yocto is, then you need to find another resource as this is not a tutorial. On the other hand if you’re here because the title made you smile then go on.

Probably you’re already know about the allwinner meta layer, meta-sunxi. Although sunxi is great and they’ve done a great job the supported boards are quite limited. On the other hand, armbian supports so many boards! But if you’re a Yocto-man then you know that this doesn’t help much. Therefore, I thought why not port the u-boot and kernel patches to the meta-sunxi layer and build images that support the same allwinner boards as armbian?

And the result was this repo that does exactly that. Though it’s still a work in progress.

https://bitbucket.org/dimtass/meta-allwinner-hx/

This repo is actually a mix of the meta-sunxi and armbian and only supports H2, H3 and H5 boards from nanopi and orange-pi. The README.mdis quite detailed, so you don’t really need to read the rest post to bring it up and build your images.

More details please?

Yes, sure. Let’s see some more details. Well, most of the hard work is already done on armbian and meta-sunxi. In the armbian build, they have implemented a script to automatically patch the u-boot and the kernel and also all the patches are compatible with their patch system. Generally, the trick with armbian is that actually deletes most of the files that it touches and apply new ones, instead of patching each file separately. Therefore, the patches sizes are larger but on the other hand is much easier to maintain. It’s a neat trick.

The script that is used in armbian to apply the patches is in lib/compilation.sh. There you’ll find two functions, advanced_patch() and process_patch_files() and these are the ones that we would like to port to the meta-sunxi layer. Other than that, armbian uses the files in config/boards/*.conf to apply the proper patches (e.g. default, next, dev). Those are refer to the  patch/kernel. There, for example, you’ll find that sunxi has the sunxi-dev, sunx-next and sunxi-next-old folders and inside each folder there are some patches. If you build u-boot and and kernel for a sunxi-supported board, then you’ll find in the output/debug/output.log and output/debug/patching.log which patches are used for each board.

Therefore, I’ve just took the u-boot and kernel patches from armbian and implemented the patching system in the meta layer. To keep things simple I’ve added the patch functions in both u-boot and kernel, instead of implement a bbclass that could handle both. Yeah, I know, Yocto has a lot of nice automations, but some time it doesn’t worth the trouble… So in my branch you’ll find the do_patch.sh script in both recipes-bsp/u-boot/do_patches.shand recipes-kernel/linux/linux-stable/do_patch.sh. Both script share the same code, which is the code that is used also from armbian. The patches for u-boot and the kernel are in a folder called patches in the same path with the scripts.

Last but not least, I’ve also to add the option to create .wic.bz2 and .bmap images. Please use them. If you want lightning-fast speed when you flash images to SD card or eMMC.

Conclusion

If you want to use Yocto to build custom destributions/images for allwinner H2, H3 and H5, then you can use this meta layer. This is just a mix of the meta-sunxi layer and the patch system from armbian, which offers a much more wider board support. For now I’ve ported most of the nano-pi boards that use H2, H3 and H5 cpus and soon I’ll do the same for the orange-pi board (update: done). For the rest boards (A10 etc) you can still use the same layer.

Also support for wic images and bmap-tools is a good to have, so use it wherever you can.

Have fun!

Driving an ILI9341 LCD with an overclocked stm32f103 (updated)

Intro

LCDs… I think LEDs and LCDs are probably the most common wet dream of people who like playing with embedded. I mean, who doesn’t like blinking LEDs and furthermore who doesn’t like to draw graphics with a microcontroller on an LCD? If you don’t press Alt+F4 now please.

LEDs are easy. Toggling a pin or flashing a LED is the first breath of every project. There you know that your microcontroller’s heart is beating properly and it doesn’t have arrhythmia. Then, driving a character LCD is a bit more harder, but still easy; but driving a graphic LCD is definitely more harder, especially if you’re starting from the scratch. Do you have to start from the scratch, though? Nah… There are many projects out there and also this is another one.

OK, so what’s the motivation behind this, if there are so many projects out there? For me it was the fact that I don’t like to use all these arduino-like stuff with my STMs, I don’t like HAL and I couldn’t find a proper cmake project that builds out of the box, without special dependencies like specific IDEs, compilers e.t.c. With this project you just download cmake and a gcc compiler, point the cmake toolchain to your gcc compiler and run build. Then it works… (maybe)

Of course, if you are regular customer here, there’s no need to say that this is a completely stupid project. It does nothing.  No, really. You won’t see any fancy graphics that other people post with their STMs in youtube. You’ll see just a yellow screen. Why? Because I just wanted to benchmark and have a template to use for any other project.

Note: I’ve updated the code and post, because I’ve added support for the xpt2046/ads7843 touch controller. I’ve used SPI2 with DMA to read the touch sensor and also I’m using the /PENIRQ interrupt pin and not polling.

Overclocking, SPI, DMA and other fancy buzzwords

If you found this by searching on the web, then you’re probably here because you know exactly what you want. SPI & DMA!!1! The reason that I like bluepill stm32 boards is that the have a lot of DMA channels and they are dirt-cheap. On top of that you can overclock it up to 128MHz!

So, why DMA is important? Well, I won’t bother you with the details here, if you need to know more than it’s much much faster, then you need to do some web-searching for the specific details, as there are people that already explain these stuff better than me. The fact is that by using DMA on SPI’s tx/rx the transfer speed sky-rockets and you can achieve the maximum available bandwidth.

On the other hand, overclocking is more interesting. The stm32f103 can be easily overclocked by just changing the PLL value. Then you can increase the main clock from 72MHz to 128MHz. Not bad, right? Especially if you think that a project which drives a graphic LCD will benefit a lot from this speed increase. I assume that you’re not crazy enough to do that on a commercial project, but if you do then you’re my hero.

In this project I’ve done some benchmarking with two different system clocks @72MHz and @128MHz and there’s a significant difference as you see later in the benchmarks.

Components

STM32

I’m using an stm32f103c8t6 board (bluepill). These modules cost less than €2 in ebay and you may have already seen me using them also in other stupid projects.

ILI9341

There is a very standard LCD module that you find in ebay and it costs around $7. It’s 2.8″ TFT, supports 240×320 resolution, it has a touch interface and an sd card holder. The part name is TJCTM24028-SPI and is the following one:

It’s a beauty, right?

USB-uart module

You need this to print the FPS count every second and also if you want to add your custom commands. You can find these on ebay with less than€1.50 and it looks like that

ST-Link

Finally, you need an ST-Link programmer to upload the firmware, like this one:

Or whatever programmer you like to use.

Pinout connections

As the LCD is not wireless you need to connect it somehow to your stm32. We’re lucky in this case, because both LCD and the stm32 have conductive pins that if they’re connected with each other in the proper way then it may work. The connections you need to do are:

STM32 ILI9341 (LCD)
PA0 LED
PA2 RESET
PA3 D/C
PA4 CS
PA5 SCK
PA6 SDO(MISO)
PA7 SDI(MOSI)
3.3 VCC
GND GND
STM32 ILI9341 (touch conrtoller)
PB8 T_IRQ
PB14 T_DO
PB15 T_DIN
PB12 T_CS
PB13 T_CLK
STM32 UART module
PA9 (TX) RX
PA10 (RX) TX
GND GND

You can power the stm32 from the USB connector.

Project source code

You can download the project source code from here:

https://bitbucket.org/dimtass/stm32f103-ili9341-dma/src/master/

All you need to do is install (or have already installed) a gcc toolchain for ARM. I’m using the gcc-arm-none-eabi-7-2017-q4-major, that you can find here. Just scroll down a bit, because there are newer toolchains; but from the tests I’ve done here, it seems this produces the most compact code. Then depending your OS, change the path of the toolchain in the TOOLCHAIN_DIR variable in the project’s file cmake/TOOLCHAIN_arm_none_eabi_cortex_m3.cmake. Last, run ./build.sh on Linux or build.cmd on Windows to build and then flash the bin/hex on the stm32.

Results

The SPI speed of the stm32f103 by using DMA can achieve an SPI clock up to 36MHz when the mcu is running on the default highest frequency which is 76MHz. That’s really fast already compared to other Cortex-M3 mcus with the same frequency (even faster mcus). To use the default 72MHz clock you need to comment out line 47 in main.c here, otherwise the clock will set to 128MHz.

This is a capture of CLK/MOSI when sending the byte 0xAA.

With the above clock settings, the stm32f103 achieves 29 FPS when drawing all the pixels of the screen.

By overclocking the mcu to 128MHz the SPI/DMA speed is much higher. To enable the oveclocking you need to un-comment line 47 here (SystemCoreClock = overclock_stm32f103();), which by default is already enabled.

This is a capture of CLK/MOSI when sending the byte 0xAA.

Now you can see that the SPI clock frequency is now ~63MHz, which is almost the double as the previous. That means that the updating of all the pixels on the screen can be done in a rate of 52 fps, which is quite amazing for this €2 board.

Touch controller (UPDATE)

I’ve lost my sleep knowing that I didn’t implemented the touch control interface, therefore I’ve updated the code and added support for the touch controller and also a calibration routine.

So, now there are two function modes. The first one (and default) is the benchmark mode and the other mode is the calibration. During the calibration mode you can calibrate the sensor and you need to do that if you want to retrieve pixel x/y values. Without calibration you’ll only get the adc sensor values, which may or may not be what you want.

To enter the calibration mode you need to send a command to the uart port. The supported commands are the following:

MODE=<MODE>
     BENCH : Benchmark mode
     CALIB : Calibration mode

FPS=<MODE>
    0 : Disable FPS display
    1 : Tx FPS on UART
    2 : Tx FPS on UART and on display

TOUCH=<MODE>
      0 : Do not Tx X/Y from touch to UART
      1 : Tx X/Y from touch to UART

The default values are MODE=BENCH, FPS=0, TOUCH=0. Therefore to enter to the calibration mode send this command to the UART: MODE=CALIB.

The calibration routine is very simple, so do not expect fancy handling in there. Even the switch statement in `ili9341_touch_calib_start()` is not needed as it’s a completely serial process. I was about to implement a state machine, but it didn’t worth it, but I left the switch in there.

So, when you enable the calibration mode, you’ll get this screen.

Then you need to press the center of the cross. Behind the scenes, this code is in the ili9341_touch_calib.c file, inside the ili9341_touch_calib_start() function, which at the state STATE_DRAW_P1 draws the screen and then waits in STATE_WAIT_FOR_P1 for the user to press the cross. I’ve  added bouncing control in the  xpt2046_polling() function, but generally the xpt2046 library doesn’t have. So the xpt2046_update() function which updates the static X/Y variables of the lib doesn’t have de-bouncing. The reason for this is that this provides a generic solution and some times de-bouncing is not always wanted, so if it does it can be implemented easily.

Anyway after pressing the point on the screen then other 2 points will be appear and after that the code will calculate the calibration data. The calibration is needed if you want to get the touch presses expressed in pixels that correspond to the screen pixels. Otherwise, the touch sensor only returns 12-bit ADC values, which are not very useful if you need to retrieve the pixel location. Therefore, by calibrating the touch sensor surface to the LCD, you can get the corresponding pixels of the pixels that are just under the pressure point.

The algorithm for that is in the touch_calib.c file and it actually derives from an application note from TI which is called “Calibration in touch-screen systems” and you can probably find it in this link. The only thing worth a note is that there are two main methods for calibration which are the 3 and 5-point. Most of the times you find the 5-point calibration method, but also the 3-point gives a good result. There a calc file in the sources source/libs/xpt2046-touch/calculations.ods that you can use to emulate the algorithm results. In this file the (X’1,Y’1), (X’2,Y’2), (X’3,Y’3) are the points that you read from the sensor (12-bit ADC values) and (X1,Y1), (X2,Y2), (X3,Y3) are the pixels in the center of each cross you draw on the screen. Therefore, in the ods file I’ve just implemented the algorithms of the pdf file. The same algorithms are in the code.

After you finish with the calibration, the calibration data are printed out in the serial port and also are stored in the RAM and they are used for the touch presses. After a reset those settings are gone. If you want to print the ADC and pixel values (after the calibration) on the serial port then you can send the TOUCH=1 command on the UART. After that every touch will display both values.

Finally, if you enable to show the FPS on the LCD you will see something weird.

This displays the FPS rate of drawing all the pixels on the screen and you see a tearing in the number, which is 51. This happens because the number print overwriting that part of the screen when is displayed and also it gets overwritten from the full display draw. The reason for that is to simplify the process and also do not insert more delay while doing the maths.

Generally, I think the code is straight forward and it serves just a template for someone to start writing his code.

Conclusion

stm32f103, rocks. That’s the conclusion. Of course, getting there is not as easy as using an arduino or a teensy board, but the result pays off the difficulty of getting there. And also when you get there is not difficult anymore.

Again, this is a really stupid and completely useless project. You won’t see any fancy graphics with this code, but nevertheless is a very nice template to create your own graphics by using an optimized SPI/DMA code for the stm32f103 and also with the overclocking included. That’s a good deal.

Of course, I didn’t wrote the whole code from the scratch, I’ve ported some code from here which is a port from the Adafruit library from the arduino. So it’s a port of a port. FOSS is a beautiful thing, right?

Have fun!