uEZ v2.02 has been released. Here are a highlight of the major changes:
- Updated to IAR 6.30 and Crossworks 2.2 compiler.
- Renesas RX63N processor added.
- NXP LPC2478 Thumb mode added for IAR compiler.
- NXP LPC1768 processor added back in.
- NXP LPC1756 processor added.
- More require/create routines added to drivers.
Other important changes/additions:
- emWin integration with the memory map has been improved.
- New CRC driver and software implementation.
If you like how the uEZ is coming along, let us know here or at firstname.lastname@example.org! Your feedback is always important to us.
uEZ v2.01 has been released and now is available at Sourceforge. So, what has changed? As with any incremental changes, this release won’t be as much as the uEZ v2.00 release, but was aimed to fix bugs. Here is a highlight of the most important bug fixes.
- Half duplex drivers have a fix with the drive enable polarity. Setting to active low now properly works.
- PLL global variables (G_ProcessorOscillator, etc.) are now stored in internal RAM (IAR compiler only). They had gotten moved to the SDRAM — but the SDRAM is not initialized until after the PLL is started.
- NOP() is now a system wide / compiler wide macro to delay instruction worth of time.
- LPC1788 A/D pin configuration is now fixed and should be working again.
- LPC1788 USB Device now handles the device interrupts correctly (stop interrupting until initialized).
- LPC2478 PLL initialization fixes a race condition.
Other important changes/additions:
- UART0 to UART3 on the uEZGUI-1788-70WVE is now part of the uEZPlatformStartup() options.
- Doxygen has been upgraded from 1.7.4 to 220.127.116.11
- Added IAP driver for uEZGUI-1788-70WVT.
- Changed to use EABI version of Segger emWin library in all builds (included in uEZ distribution)
There are more changes, so be sure to check out the Change History document. Let us know if you are having any problems at email@example.com.
One of most interesting parts of uEZ is how it makes use of double pointers to track device driver workspaces and APIs. Unfortunately, for many software engineers that see the double pointers, they get confused on what is happening and are tempted to go around the system. This article will outline what is happening and how it works and why we choose to use this method.
But before we get into the details of double pointers, a bit of background information is required. When we designed uEZ, we had a few goals we wanted to achieve. First, we wanted to create a system that facilitated reusable code regardless of processor and hardware. We had fallen into a pattern of creating code and libraries for each design we built and although we could sometimes use the code from a previous project, a certain amount of grind was being hit on each project that we knew we could remove. Second, we wanted to create a system that was easy to extend and modify as new features become available. Just because we are reusing code didn’t mean we did not have new requirements. We wanted a framework that grew as new features were determined necessary. Plus, we didn’t have to design everything possible in version 1.00. Third, we wanted to use the most common language for embedded software. For this age, it is current C. Although we desired an object oriented language such as C++, we could not justify the move as many issues with compilers and general community acceptance still exists.
But we still wanted to use many of the features of an object oriented language in C. So, we devised a system of classes and objects which became known in uEZ as interfaces (classes) and workspaces (objects). We changed the names to reflect the fact that these objects and classes are specific to the requirements of the device driver system and should not be thought of as a generic system.
So with the background out of the way, we can return our focus on the double pointer system of uEZ. So, what do we really mean when we say uEZ uses double pointers? Let’s deconstruct what is happening….
When we write “DEVICE_Something **”, the “**” is a double pointer. A better way to say “double pointer” is “pointer to pointer”. For a moment, let’s take this at the surface – A pointer to a pointer. Although a pointer is merely an address in memory, it has a second component – a type – that is tracked by the compiler. So, again, when we write “DEVICE_Something **” we get a pointer to a pointer to a DEVICE_Something structure.
Ok … why? The “DEVICE_Something **” in the above example is really the interface (class) to the device driver workspace (object). Where is this workspace object? The object (called “Workspaces” in uEZ) is in the first dereferencing of the pointer to the pointer. We get it by type casting the pointer to an object. A workspace (“object”) is a block of data that starts with a pointer to its interface (“class”). Therefore, we get something that looks like this:
So, for the price of one double pointer, we get two pieces of information – workspace object and interface class. In theory, we could have just called it “Workspace *” and the first field of all objects contain a pointer to “Interface”. Instead, we wanted to use the type of the pointer to help define what type of object we are dealing with much in the same way in object oriented projects passing around variables of a specific class. By type casting the pointer and deference, all information about that object can be extracted.
*(DEVICE_Something **) = ptr to interface (or cast to T_uezDeviceInterface *)
(DEVICE_Something **) = ptr to workspace (or cast to T_uezDeviceWorkspace *)
*(DEVICE_Something **) -> function = ptr to function in interface
Each function in the API needs access to the workspace, so we pass in the double pointer as the first parameter. The function can then typecast it to its private workspace with code like the following:
T_Temperature_AnalogDevices_ADT7420_Workspace *p =
A workspace is merely a block of data allocated with the singular requirement of a having a pointer to its interface at the start – effectively making it a subclass of T_uezDeviceWorkspace.
So, is all of this double pointer code worth it? In the recent uEZ v2.00 release, almost all of the drivers have been hidden by the uEZ System functions and handles to the devices. Although we could have continued to use a handle terminology, we wanted the device drivers to be as fast as possible and not require another subsystem to call for every function call.
What do you think? Should we abandon the double pointers for another system or should we keep this system? Do you have a problem with double pointers? Or would more and better documentation be enough?
Welcome to uEZings! From time to time, the folks from Future Designs, Inc. will post information and commentary on the what we’re doing with our middleware embedded software solution called uEZ (“Muse”). I intend to keep our posts informal but informative. When you see something you like … or even possibly what you don’t … feel free to voice your opinion here.
So, on with the show/blog ….
uEZ v2.00 Release
Today we have released a new version of uEZ and many you are wondering, “What’s with the 2.00 number? Weren’t we just on v1.12 or so? Is this going to make everything I’ve done with uEZ become incompatible?”
The quick answer is that uEZ v2.00 is v1.12 plus a whole lot more. So much more, I think you will want to change to the new design — thus, the v2.00. Let me give you the details.
uEZ v2.00 is a Library
The previous version of uEZ were built along the idea that uEZ would be compiled into each and every project. In fact, the original design of uEZ was to be code where every file is included in a project and with one handy #define, you can change from one processor to another. Alas, that design turned out to be much harder than we expected. With the need to support different compilers and assembly language files as well as general linker and other configuration files, we quickly found out each multiple projects based on processor and compiler type were necessary. So, we split up uEZ into multiple builds.
We still kept close to our heart the idea that we could still somehow have one big monolithic blob of code where #define’s could turn on and off the features needed and so we added multiple config files until we ended up with four: Config_Processor.h, Config_Platform.h, Config_App.h, and Config_Build.h. But feedback has shown that people just like it and find them hard to use. If anything, developers want the whole thing to Drop In and Just Work(TM). Add to the complex configuration several hundred files and developers start to get a bit overwhelmed.
So, like a graphics library, we are changing uEZ to be just that — uEZ v2.00 is now a library. If you look in the /uEZ/Build/Generic directory, you’ll find a tree of multiple processor, RTOS, and compiler types that can be compiled into two easy libraries: Debug and Release. To make the process even easier, we’re also providing uEZ in precompiled libraries you can link with your project.
uEZ v2.00 is Easier to Configure Hardware with Require functions
Since we went to a library, we could no longer depend on #define tricks that would change the code from one setting to the next. If you want access to the SPI0 bus, you no longer can just set UEZ_ENABLE_SPI0 to 1 in your configuration files and have it turned on. Additionally, we only want the SPI code to be in the project if it is needed. Therefore, we have chosen to depend on the linker to pull in the needed code through what we call “Require” functions. A require function merely is a routine that adds the device driver or subsystem to uEZ at startup. Require functions only work once. If the same require function is called twice, it will ignore the second call. A SPI0 driver can be required multiple times in the code and it will be initialized on the first requirement.
Require functions also declare what the configuration will be for that device. For example, on many processors, the SPI can be connected to 1 of several pin locations. The require function declares which pins to connect to that SPI peripheral. On the NXP LPC1788, this can be done in a simple function as follows:
LPC1788_SPI0_Require(GPIO_P2_22, GPIO_P2_26, GPIO_P2_27, GPIO_NONE);
In this example, the four parameters represent the SCK, MISO, MOSI, and SSEL pins for the LPC1788. Since we only use the first 3 pins, the fourth is left unconfigured and available for another device. As well, this registers the HAL driver for the SPI0.
We’ve also added new logic to the GPIO system. As shown above, all pins on a CPU can now be referenced by a single 16-bit value. No more passing around HAL_GPIOPort ** and a pin index. Routines such as UEZGPIOSet() and UEZGPIOClear() make toggling a pin easy to handle. Although not all of our drivers have been changed over, we’re working on using it more and more. More importantly, GPIO pins can be “locked” when configured. All require functions lock any GPIO pins used with the command UEZGPIOLock(). The pin is marked as used when locked the first time. If another service or peripheral tries to the lock the pin again, an error is flagged on the LCD as a BSOD (Blue Screen of Death). You’ll no longer have to be debugging code only to find that the pin was double assigned and not in the initial mode you expected.
In the past, we put it on the developer to setup all the pins in the routine UEZBSP_CPU_PinConfig() possibly using a “pin config” file. This is no longer the case. Going forward, all HAL uEZ drivers will lock and configure the pins needed for that peripheral.
uEZ v2.00 is Easier to Configure Platforms
Require functions are also in the platforms (specific hardware implementations). The platform uses the above require functions to setup the device drivers that interface to the low level HAL drivers. Routines such as UEZPlatform_Serial0_Require() not only create a device driver for serial #0, but also connects it to the proper HAL serial driver for your hardware.
These uEZPlatform require functions can be thought of small installers who not only bring up the device driver but also bring up anything else needed for that functionality. For example, when using an LCD, the UEZPlatform_LCD_Require() routine will pull up the LCD Controller, Backlight generator, and power on/off control lines. Application developers can then call UEZPlatform_LCD_Require() in any system and know that the LCD has been setup and all requirements met before continuing.
An added advantage of doing it this way is only the code needed is linked into the source code. If an application never needs the LCD, then the backlight code is not needed either.
uEZ just requires a single routine in your application code to setup the platform called uEZPlatformStartup(). It calls all the standard uEZ requirement routines for devices needed. In the FDI platforms, we help shorten this routine by also including a bulk requirement routine called UEZPlatform_Standard_Require() who calls a large number of the standard requirement routines we believe all users will need for a project. A version called UEZPlatform_Minimal_Require() has been created for small implementations and UEZPlatform_Full_Require() added for those who want everything uEZ can offer on that platform.
To support these functions, all our platforms have been updated to with require functions and we hope you like them. Many of the bug fixes can be carried back to previous editions, but we recommend going forward to uEZ v2.00.
Although I could go on and on about all the little bugs and changes we’ve made to uEZ, these are the big changes: library builds, require functions, easy to configure platforms, and changed GPIO system. The basic uEZ v1.12 is still there, but now it is easier to access. Take a look and tell us what you think.
And one last thing: Be sure to check out the file “uEZ v2.00 New Project Creation Guide.pdf” for getting started with a new project using the newly added uEZ v2.00 Templates.