Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / IoT / Raspberry-Pi

More Fun, frustration and laughs with the Pi .. Part II

4.86/5 (21 votes)
15 Dec 2016CPOL13 min read 31.3K   892  
More playing, crying and laughing with the Raspberry Pi

*** NEW Version coming USB  operational (see bottom)

Precompiled Pi2 binary but untested. I used these settings "-mfpu=neon -mfloat-abi=hard -march=armv7-a -mtune=cortex-a7" which I belive is correct. Would love somebody with a Pi2 to check it :-)

Precompiled Pi3 binary but as with the Pi2 untested. I used these settings "-mfpu=neon-vfpv4 -mfloat-abi=hard -march=armv8-a -mtune=cortex-a53". I am getting one warning which I will look into off line 131 of the assembler start file.

armc-start.S:131: This coprocessor register access is deprecated in ARMv8

Introduction

In my last article I ended with a lot of frustration with the Raspberry Pi but you know the story with an embedded programmer we thrive on a challenge.

So the good news is I got everything and a bit more than I had hoped to get working this weekend. Here is a screenshot of current workpoint and I will provide source code as well as diskimage at top of the article.

Image 1

So that is a few graphics primitives Quad and Cubic Beziers as used by TrueType fonts. Basic windows and text and a debugging console. Then I have the code dropping thru to my Darkside code of my last article and the timers start and the messages post producing the result I expected. I will have more to say about all this later on in the article.

Image 2

Background

All that wasn't fun and my biggest lesson was don't trust any code out there in C for the raspberry PI because most obviously isn't written by engineering type who specialize in embedded. This is going to to be a lot more of a challenge if you aren't of that ilk because there are a lot of errors and lots of half hacks.

So in this article I am going to cover

  1.  Toolchains I am using on windows as I use them, you are welcome to use others and a different O/S but I leave you to your own devices in that situation.
  2.  Access to hardware registers under C and the keywords VOLATILE and CONST and what they do
  3.  State of the current code what it is and what it isn't.
  4.  Immediate plans for me and possible more articles.
  5. Future and ideas etc

ToolChain

So I am programming on Windows and for my editor I am using Visual Studio 2015 community. It is what I use for all my normal coding and I know it well and it is first rate (aside from the install which is perhaps the worst installer ever written).

The actual compiling of the code is done via the windows gcc-arm-none-eabi release from arm. I use the latest version 5.4 from the official arm site via this link

https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads

There are many who say to use the old 4.7 version because they think the later versions are bugged. In my playing around I actually found the 4.7 version is actually more bugged. This silly statement is based on their experience with the code style they are using, and we will discuss this in more depth in the next section. For now lets get back to the toolchain.  I installed the download into a compiler tool directory and you need to record/remember the path to that directory  which I will refer to as $TOOLPATH

Now I set up a solution directory which will be where I am going to put all my code and whatever else makes up this project I am working on.  I will call that the $SOLUTIONPATH. I didn't make those up that is what visual studio calls them and so I am just following suit.  In my solution directory I have a single MAKE.BAT file which I will edit as need.  When you download the sample code you will see all that however the $TOOLPATH will be wrong for you

For this current MAKE.BAT looks like this

C++
@REM COMPILER COMMAND LINE
..\gcc_pi_5_4\bin\arm-none-eabi-gcc -O2 -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s -nostartfiles -g -Wl,-T,rpi.x main.c armc-cstubs.c armc-start.S RPi-Hardware.c Darkside.c User.c -o kernel.elf -lc -lm
@echo off
if %errorlevel% EQU 1 (goto build_fail)

@REM LINKER COMMAND LINE
@echo on
..\gcc_pi_5_4\bin\arm-none-eabi-objcopy kernel.elf -O binary kernel.img
@echo off
if %errorlevel% EQU 1 (goto build_fail) 

echo BUILD COMPLETED NORMALLY
exit /b 0

:build_fail
echo ********** BUILD FAILURE **********
exit /b 1

The file is pretty simple we have a single call to the compiler with a lot of parameters. If the compiler returns and error it jumps down to build failure. If it is successful it runs forward to the linker and it's command lines. If that program exits with an error it also jumps to teh build failure. If it all worked you will get "BUILD COMPLETED NORMALLY" other wise you will get "********** BUILD FAILURE **********"

You can run the batch file by just clicking on it in the directory if you like but I will show you how I integrate that into Visual Studio.

For the moment we need to talk about the $TOOLPATH which in my .bat file above is "..\gcc_pi_5_4\bin\arm-none-eabi-gcc". The reason for that is on my machine I installed the arm tools to G:\PI\gcc_pi_5_4 and my solution directory is G:\PI\Darkside which is where the .BAT is located. That path I have used is the relative path to get to the compiler file "arm-none-eabi-gcc.exe" from my solution directory where the .BAT is. If you get lost or stuck use the absolute path, so for my example I could have used "g:\pi\gcc_pi_5_4\bin\arm-none-eabi" instead of ".\gcc_pi_5_4\bin\arm-none-eabi-gcc". Anyhow the long and short wou will need to change the path on the compiler and linker lines. If you are using a PI2 or PI3 you also need to select the right processor.

To integrate that into visual studio i simply got the menu option to add external tool and you can see what I typed in. There you see I just setup the toolpath and solution directories to what I physically have on my machine. You will also note I have ticked the use console window so the batch file will report into my console window.

Image 3

Done correctly you get your item on the external tool list. If you want to do the next step note the number it is in the list. In my case 6 .. count from Create GUID.

Image 4

Using the instructions in the next link you can add an icon on the toolbar for your external tool (you need that number from above .. which was 6 for me).

https://blogs.msdn.microsoft.com/david_kidder/2013/09/18/adding-a-button-to-the-debug-toolbar-in-visual-studio/

If it's all done right when you click on the icon it will fire off and compile and you will see this in the usual output window

Image 5

VOLATILE AND CONST

As I discussed there is lots of talk about this or that version of GCC compiler toolchain being bugged and it is and it isn't :-)  This is something proper embedded programmers run across all the time. So lets start at the beginning and first start with the recommendation as given by ARM.

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3736.html

Now when an embedded vendor tool provider gives you a recommendation I would strongly suggest, unless you have a good reason or objection you follow the standard. Yes I know the strict C standard says you can put things in different orders, but they are not responsible for the maintenance of the toolchain like the vendor is. You can complain it's a bug OR just use the standard.

int volatile * const ptr;
 |     |     |   |    |
 |     |     |   |    +------> ptr is a
 |     |     |   +-----------> constant
 |     |     +---------------> pointer to a
 |     +---------------------> volatile
 +---------------------------> integer

So they want "type" "volatile" "ptr" "const" "name" when setting a pointers to hardware. Again I know the C standard says they don't have to be in that order but the vendor made a suggestion, AKA DO IT, STOP ARGUING. For me it was trivial because I always do it like that because of the next problem in many code I see on internet.

MACRO dramas with volatile. Much of the Rapberry code out there doesn't put brackets correctly around the MACRO  the use for register access. If you put the volatile at the front without a correct bracket the macro expansion puts the volatile on the wrong thing. Here is the command line to check MACRO exapnsion 

C++
gcc -E -dD

So the proper expansion for 32 bit access to hardware should look like:

C++
static uint32_t volatile* const readwritereg = (uint32_t volatile*)(0x.... addresss..);

// The readonly register version is

static uint32_t const volatile* const readonlyreg = (uint32_t const volatile*)(0x....address...);

Lets explain the bits and what it protects.

STATIC:  Is used to tell the compiler this pointer is only used here, if it isn't touched it can be removed along with any code that uses it. So if you have a big library and code in it isn't used at build time it will remove it. The only time you don't have this is if you are going to expose the pointer on a .H file interface.

uint32_t is our type in this case, you may have int (like in the arm example) or long or whatever type represents your register. 

VOLATILE:  is placed after the type as our vendor requested and also protects us from MACRO expansion dramas. In the read only we have "const volatile" which tells the compiler it can't write to it but it is volatile. Technically you can put them the other way around but the logic goes the compiler runs before the optimizer. The compiler wants and will see the const and ignore the volatile. The volatile is for the optimizer to make sure it keeps accesses to the hardware. So keep them in the order they will be processed "const volatile" being compiler then optimize, there is less likelyhood of drama.

CONST: Okay this const stops us from writing to the  pointer itself. Something like this will invoke a compiler error

readwritereg = (uint32_t volatile*)0x4000;

There is nothing actually wrong with that code I am setting the register pointer to a new address only I know where my register is why would I be changing it's address (unless that was what I wanted to do). Usually what it means is you used two libraries and both libraries chose the same name for different registers something common say like ARM_TIMER. In different libararies you will be amazed at the use of that with totally different addresses because there are multiple timers on the ARM.

That const is protection not so much for you but against others that have scope to your register address being able to change it and crash your code. So put it in unless you have the strange situation that your register moves around. 

The rest should be obvious. Under that scheme there is one problem you can't protect against a write only register being read.

For the more advanced users there is a better schema for compiler which accept anonymous unions I will leave you to work thru what it does

C++
struct wo_uint32 { uint32_t volatile write; };                        // Hardware unsigned 32 bit write only register
struct ro_uint32 { uint32_t const volatile read; };                    // Hardware unsigned 32 bit read only register
struct rw_uint32 { union {
                    uint32_t const volatile read;                   // Hardware unsigned 32 bit read/write register in read mode
                    uint32_t volatile write;                        // Hardware unsigned 32 bit read/write register in write mode
                   };
                 };

typedef struct rw_uint32* hw_uint32_ptr;                            // Hardware pointer unsigned int 32 bit (read/write)
typedef struct ro_uint32* hw_ro_uint32_ptr;                            // Hardware pointer unsigned int 32 bit (read only)
typedef struct wo_uint32* hw_wo_uint32_ptr;                            // Hardware pointer unsigned int 32 bit (write only)

// LETS SHOW A USE 
static hw_uint32_ptr const arm_sys_timer_lo = (hw_uint32_ptr) (RPI_SYSTIMER_BASE + TIMER_LO_OFFSET);
uint32_t lowCount = arm_sys_timer_hi->read;  // read the timer
arm_sys_timer_hi->write = 0x555;             // write to the timer       

Many of what people are describing as bugs is the compiler optimizing away sections of code. It's a common problem with all compilers with hardware and if you want a more detailed paper on the subject here is a start

http://www.cs.utah.edu/~regehr/papers/emsoft08-preprint.pdf

A couple of the files from the Valvers tutorials have the problem. Again I say the code Brian has written is fine from a C point of view it just falling thru implemetation or bugs with the vendor tools (your choice of words). I respect the great work Brian did and it would be wrong to place the blame on him.

My final whinge in this section is PLEASE STOP REDEFINING TYPES this standard library defines all the normal bit types and look it's 1 line to include it.

C++
#include <stdint.h>

Yes O/S API's define things like LONG, BOOL, UNSIGNED LONG blah blah blah but they have a good reason for doing that because those sizes can change.  A BOOL can be  8 bits, 16 bits , 32 bits or even 64 bits depending on what processor the O/S is on. When you are dealing with hardware registers they have fixed sizes in bits on the ARM and THEY NEVER CHANGE AND CAN'T CHANGE so use <std.int> for the love of all things holy as it is the C standard.

Code status

The current code status or the Raspberry Pi stuff could best be described as horrific. I basically grabbed all the code snippets I wanted and added them into a single file group   RPi_Hardware_H + RPi_Hardware.C . This was based on finding the code libraries in there current form basically all with problems detailed above. I am slowly working thru the file and fixing everything.

The first fix I implemented hopefully correctly was to get the code to switch between the various models of RPi based on the processor you choose. I only have a Pi 1 so if someone can let me know that this translates correctly for Pi2 & 3 would be nice.

C++
#ifdef __ARMEL__                                // Compiling for ARM ... likely Rasberry PI
#if (__ARM_ARCH == 6)                            // Architecture is ARM 6 so its a Original Pi A or B+
#define RPI                                        // Define Rasberry Pi
#endif
#if (__ARM_ARCH == 7)                            // Architecture is ARM 7 so its a Pi 2
#define RPI2                                    // Define Rasberry Pi 2
#endif
#if (__ARM_ARCH == 8)                            // Architecture is ARM 8 so its a Pi 3
#define RPI3                                    // Define Rasberry Pi 3
#endif
#endif

All the address changes swing off those implementations so as long as you select the right processor I believe you get the right code addressed.

My Play Code

Please treat the code as a pre-release I am not offereing great detail at this point as it will all change in the release I will do properly. The code as it is for now is for those who want to jump ahead of me releasing. 

The console is a very basic implemented terminal but all console IO should be pumped to it. It is your responsibility to keep it the front with a PiConsole.Redraw if needed. The text is buffered within the console. The size I have set it to is my chocie so I have some area of the screen to play in

#define ROWS    10
#define COLUMNS 80

I implemented a complete integer version of a QUAD and CUBIC BEZIER using Euler mathematics. There are no floats used in the routines at all but they suffer the usual problem they create rounding steps. The usual trick on embedded processors is to back off the loop delta so you get small segments to make the effect less noticable. The current delta step resolution is 1:1.  What I have found is the video unit has what looks like an OpenGL hardware renderer and if so I intend to implement antialiasing in it.

The truetype letter B glyph is included from test.inc for me to test. The scan line fill routine is first using floats/doubles and second reversed for me to look at problems. It is not intended to be used but simply something for me to check TrueType work with. If there is anything useful for you in the code you are welcome to it but I do not need feedback on it.

The screen font comes from the included file "BitFont.Inc" you can read it in a text editor. It is a 4096 byte array of 256 characters each of 16 bytes and it is DOS code page 850 layout for character positions. For English language it will be fine but sorry foreign language people you will struggle. Each character is a byte (8 bits wide) and the characters are 16 bytes high. You can't change it without including a different binary font.

Future for me

I would first like to have a big shout out to Brian Sidebotham for his valvers site which has helped get me to this point.

My immediate next tasks will be to get the USB up and running and get Mouse and Keyboard online. The USPI library is freely available but a quick look at the code gave me nightmares for many of the reasons discussed above.  Lets just say the arm GCC compiler and that code may not agree, we will leave who is wrong out. If you are interested in USPI here is the link

https://github.com/rsta2/uspi

The two big Elephants in the room remain. One, getting any real performance out of the video unit and two, being able to read/write to the SD card both of which are laced with NDA documentation problems.

At least with the console up I can probe and test things. However a keyboard so I could type would be more than helpful which is why the USB has been prioritized.

Anyhow I hope the code is of some help to you  and doesn't give you too many dramas.

History

Version Alpha 0.1 :  A really bad but running start point.

Update

After two weekends fighting with USB new version coming. Mouse and Keyboard both line creating correct WM_xxxxxx windows messages. Update 3 in process of being submitted.

Image 6

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)