embedded software boot camp

Configuring hardware – part 1.

Saturday, November 13th, 2010 by Nigel Jones

One of the more challenging tasks in embedded systems programming is configuring the hardware peripherals in a microcontroller. This task is challenging because:

  1. Some peripherals are stunningly complex. If you have ever configured the ATM controller on a PowerQUICC processor then you know what I mean!
  2. The documentation is often poor. See for example just about any LCD controller’s data sheet.
  3. The person configuring the hardware (i.e. me in my case) has an incomplete understanding of how the peripheral works.
  4. One often has to write the code before the hardware is available for testing.
  5. Manufacturer supplied example code is stunningly bad

I think I could extend this list a little further – but you get the idea. Anyway, I have struggled with this problem for many years. Now while it is impossible to come up with a methodology that guarantees correct results, I have come up with a system that really seems to make this task easier. In the first part of this series I will address the most elemental task – and that is how to set the requisite bits in the register.

By way of example, consider this register definition.

This is a control register for an ADC found in the MSP430 series of microcontrollers. The task at hand is how to write the code to set the desired bits. Now in some ways this is trivial. However if you are serious about your work, then your concern isn’t just setting the correct bits – but doing so in such a manner that it is crystal clear to someone else (normally a future version of yourself) as to what you have done – and why. With this as a premise, let’s look at some of the ways you can tackle this problem.

Magic Number

Probably the most common methodology I see is the magic number approach. For example:

ADC12CTL0 = 0x362C;

This method is an abomination. It’s error prone, and very difficult to maintain. Having said that, there is one case in which this approach is useful – and that’s when one wants to shutdown a peripheral. In which case I may use the construct:

ADC12CTL0 = 0;   /* Return register to its reset condition */

Other than that, I really can’t see any justification for this approach.

Bit Fields

Even worse than the magic number approach is to attempt to impose a bit field structure on to the register. While on first glance this may be appealing – don’t do it! Now while I think bitfields have their place, I certainly don’t recommend them for mapping on to hardware registers. The reason is that in a nutshell the C standard essentially allows the compiler vendor carte blanche in how they implement them. For a passionate exposition on this topic, see this comment on the aforementioned post. Anyway, this approach is so bad I refuse to give an example of it!

Defined fields – method 1

This method is quite good. The idea is that one defines the various fields. The definitions below are taken from an IAR supplied header file:

#define ADC12SC             (0x001)   /* ADC12 Start Conversion */
#define ENC                 (0x002)   /* ADC12 Enable Conversion */
#define ADC12TOVIE          (0x004)   /* ADC12 Timer Overflow interrupt enable */
#define ADC12OVIE           (0x008)   /* ADC12 Overflow interrupt enable */
#define ADC12ON             (0x010)   /* ADC12 On/enable */
#define REFON               (0x020)   /* ADC12 Reference on */
#define REF2_5V             (0x040)   /* ADC12 Ref 0:1.5V / 1:2.5V */
#define MSC                 (0x080)   /* ADC12 Multiple Sample Conversion */
#define SHT00               (0x0100)  /* ADC12 Sample Hold 0 Select 0 */
#define SHT01               (0x0200)  /* ADC12 Sample Hold 0 Select 1 */
#define SHT02               (0x0400)  /* ADC12 Sample Hold 0 Select 2 */
#define SHT03               (0x0800)  /* ADC12 Sample Hold 0 Select 3 */
#define SHT10               (0x1000)  /* ADC12 Sample Hold 1 Select 0 */
#define SHT11               (0x2000)  /* ADC12 Sample Hold 1 Select 1 */
#define SHT12               (0x4000)  /* ADC12 Sample Hold 2 Select 2 */
#define SHT13               (0x8000)  /* ADC12 Sample Hold 3 Select 3 */

With these definitions, one can now write code that looks something like this:

ADCT12CTL0 = ADC12TOVIE + ADC12ON + REFON + MSC;

However, there is a fundamental problem with this approach. To see what I mean, examine the comment associated with the define REF2_5V. You will notice that in this case, setting the bit to zero selects a 1.5V reference. Thus in my example code, I have implicitly set the reference voltage to 1.5V. If one examines the code at a later date, then it’s unclear if I intended to select a 1.5V reference – or whether I just forgot to select any reference – and ended up with the 1.5V by default. One possible way around this is to add the following definition:

#define REF1_5V             (0x000)   /* ADC12 Ref = 1.5V */

One can then write:

ADCT12CTL0 = ADC12TOVIE + ADC12ON + REF1_5V + REFON + MSC;

Clearly this is an improvement. However there is nothing stopping you writing:

ADCT12CTL0 = ADC12TOVIE + ADC12ON + REF1_5V + REFON + MSC + REF2_5V;

Don’t laugh – I have seen this done. There is also another problem with the way the fields have been defined, and that concerns the fields which are more than 1 bit wide. For example the field SHT0x is used to define the number of clock cycles the sample and hold should be active. It’s a 4 bit field, and thus has 16 possible combinations. If I need 13 clocks of sample and hold, then I have to write code that looks like this:

ADCT12CTL0 = ADC12TOVIE + ADC12ON + REF1_5V + REFON + MSC + SHT00 + SHT02 + SHT03;

It’s not exactly clear from the above that I desire 13 clock samples on the sample and hold. Now clearly one can overcome this problem by having additional defines – and that’s precisely what IAR does. For example:

#define SHT0_0               (0*0x100u)
#define SHT0_1               (1*0x100u)
#define SHT0_2               (2*0x100u)
...
#define SHT0_15             (15*0x100u)

Now you can write:

ADCT12CTL0 = ADC12TOVIE + ADC12ON + REF1_5V + REFON + MSC + SHT0_13;

However, if you use this approach you will inevitably end up confusing SHT00 and SHT0_0 – with disastrous and very frustrating results.

Defining Fields – method 2

In this method, one defines the bit position of the fields. Thus our definitions now look like this:

#define ADC12SC             (0)   /* ADC12 Start Conversion */
#define ENC                 (1)   /* ADC12 Enable Conversion */
#define ADC12TOVIE          (2)   /* ADC12 Timer Overflow interrupt enable */
#define ADC12OVIE           (3)   /* ADC12 Overflow interrupt enable */
#define ADC12ON             (4)   /* ADC12 On/enable */
#define REFON               (5)   /* ADC12 Reference on */
#define REF2_5V             (6)   /* ADC12 Ref */
#define MSC                 (7)   /* ADC12 Multiple Sample Conversion */
#define SHT0                (8)   /* ADC12 Sample Hold 0 */
#define SHT1                (12)  /* ADC12 Sample Hold 1 */

Our example configuration now looks like this:

ADCT12CTL0 = (1 << ADC12TOVIE) + (1 << ADC12ON) + (1 << REFON) + (0 << REF2_5V) + (1 << MSC) + (13 << SHT0);

Note that zero is given to the REF2_5V argument and 13 to the SHT0 argument. This was my preferred approach for a long time. However it had certain practical weaknesses:

  1. It relies upon the manifest constants being correct / me using the correct manifest constant. You only need to spend a few hours tracking down a bug that ends up being an incorrect #define to know how frustrating this can be.
  2. It still doesn’t really address the issue of fields that aren’t set. That is, was it my intention to leave them at zero, or was it an oversight?
  3. There is often a mismatch between what the compiler vendor calls a field and what appears in the data sheet. For example, the data sheet shows that the SHT0 field is called SHT0x. However the compiler vendor may choose to simply call this SHT0, or SHT0X etc. Thus I end up fighting compilation errors because of trivial naming mismatches.
  4. When debugging, I often end up looking at a window that tells me that ADC12CTL0 bit 6 is set – and I’m stuck trying to determine what that means. (I recognize that some debuggers will symbolically label the bits – however it isn’t universal).

Eschewing definitions

We now come to my preferred methodology. What I wanted was a method that has the following properties:

  1. It requires me to explicitly set / clear every bit.
  2. It is not susceptible to errors in definition / use of #defines.
  3. It allows easy interaction with a debugger.

This is what I ended up with:

ADC12CTL0 =
 (0u << 0) |        /* Don't start conversion yet */
 (0u << 1) |        /* Don't enable conversion yet */
 (1u << 2) |        /* Enable conversion-time-overflow interrupt */
 (0u << 3) |        /* Disable ADC12MEMx overflow-interrupt */
 (1u << 4) |        /* Turn ADC on */
 (1u << 5) |        /* Turn reference on */
 (0u << 6) |        /* Reference = 1.5V */
 (1u << 7) |        /* Automatic sample and conversion */
 (13u <<  8) |      /* Sample and hold of 13 clocks for channels 0-7 */
 (0u << 12);        /* Sample and hold of don't care clocks for channels 8-15 */

There are multiple things to note here:

  1. I have done away with the various #defines. At the end of the day, the hardware requires that bit 5 be set to turn the reference on. The best way to ensure that bit 5 is set is to explicitly set it. Now this thinking tends to fly in the face of conventional wisdom. However, having adopted this approach I have found it to be less error prone – and a lot easier to debug / maintain.
  2. Every bit position is explicitly set or cleared. This forces me to consider every bit in turn and decide what it’s appropriate value should be.
  3. The layout is important. By looking down the columns, I can check that I haven’t missed any fields. Just as important, many debuggers present the bit fields of a register as a column just like this. Thus it’s trivial to map what you see in the debugger to what you have written.
  4. The value being shifted has a ‘u’ appended to it. This keeps the MISRA folks happy – and it’s a good habit to get into.
  5. The comments are an integral part of this approach

There are still a few problems with this approach. This is what I have discovered so far:

  1. It can be tedious with a 32 bit register.
  2. Lint will complain about shifting zero (as it considers it pointless). It will also complain about shifting anything zero places (as it also considers it pointless). In which case you have to suppress these complaints. The following macros do the trick:
#define LINT_SUPPRESS(n)  /*lint --e{n} */
LINT_SUPPRESS(835)        /**< Inform Lint not to worry about zero being given as an argument to << */
LINT_SUPPRESS(845)        /**< Inform Lint not to worry about the right side of the | operator being zero */

In the next part of this article I will describe how one can extend this technique to make configuring peripherals a lot less painful.

32 Responses to “Configuring hardware – part 1.”

  1. Jeff Gros says:

    Hi Nigel,

    It’s always fun to look at the different coding styles people use. I’m sure everyone can find fault in every single method if they try hard enough. I use a method that is similar to “method 1”. I’ll post it below. Hopefully it won’t look like a mess after I post it.

    // ADC12 Control Register 0
    ADC12CTL0 = 8*SHT10 // sampling time for mem8 .. mem15 is 256 clocks
    | 8*SHT00 // sampling time for mem0 .. mem7 is 256 clocks
    | 1*MSC // multiple conversion
    | 1*REF2_5V // select internal 2.5V reference
    | 1*REFON // turn on internal reference
    | 1*ADC12ON // turn on ADC core (100 ns settling time after reference settles)
    | 0*ADC12OVIE // overflow interrupt disabled
    | 0*ADC12TOVIE // conversion-time-overflow interrupt disabled
    | 0*ENC // do not enable conversion yet
    | 0*ADC12SC; // do not start conversion yet

    Basically the idea is that every bit should be defined based on the least significant bit for that “field”. The SHT1x field has 4 bits, and starts at 0x1000, so we use SHT10. I then multiply whatever setting I want against that bit name to make the setting. The advantage of this method is that it is very easy to try out different settings; just change the number being multiplied to the base definition, and update the comment that goes with it.

    By defining all bits in the register, it should avoid the “duplicate definitions” error. Create the definition once, and change the number being multiplied, not the base definition.

    As far as confusing SHT00 and SHT0_0, that is still a major vulnerability. When I start a new project I always double check the bit definitions of all my old modules I’m using, as I have found erroneous definitions in the past. In fact, the code I pulled this from, which was a very old project, happened to have this exact error (to be precise, the mixup was SHT00 vs SHT0_1, which are functionally equivalent, but still it offended my sensibilities)!

    • Nigel Jones says:

      I’ve seen this technique used. I think it’s decent – and if you really want to use #defined field names then it’s probably the best of the bunch. As you say, it is still vulnerable to #define errors. IMHO it’s also difficult to tell if you have indeed defined every field. For example if you had accidentally left out the MSC field it would be impossible to tell other than looking at the register definition.

      I also noticed that you went from MSB->LSB whereas I tend to prefer LSB->MSB. I can’t claim to have been consistent in this over the years; nor can I claim that one is better than the other. Probably the best advice I can offer in this situation is to look and see how your debugger does it – and adopt their methodology.

  2. GroovyD says:

    how do you guys feel about caching the register values in code? for example say you setup an accelerometer to a gain of 8x initially and then later you want it at 1x and of course basically you need to clear a few bits or set a few others. normally i would read in the register and change the bits and write it out, 2 accesses (often over i2c) whereas i could have kept a shadow copy and then just done the write, 1 access. but you can never be sure things havent changed. actually you usually are sure they havent but i was wondering if this creates a potential for trouble?

    for my work i am always writing peripheral drivers and havent settled on what ‘the best’ approach is. there is certainly an advantage to shadowing in terms of getting back to sleep to save power or even just not blocking the rest of the app during the intermediate accesses.

    in terms of setting the value itself i tend to the short and simple approach reg | = 0x80 // set 8x gain assuming if i ever have to look back at the code i can pretty easily figure it out again to make any changes. either that or i haven’t found that putting in the significant extra time up front saves it down the line.

    • Nigel Jones says:

      I regularly cache off-chip registers. For example a project I am working on has an SPI accessed VFD and an I2C accessed keyboard controller. For both of these peripherals I cache the registers so as to avoid having to do a read – modify – write across the bus. If you think about it, if you are concerned that the register value might change (making the cached copy wrong), then logically it may also change between the read and the write. Of course if your concern is that the cached copy may change unexpectedly then you have bigger problems.

  3. GroovyD says:

    as well i was also wondering if you do a read-back of register values you set as an assurance? recently i started using a routine i wrote that has one parameter for bits to clear and another for bits to set and it basically reads the register in, clears the clear bits and sets the set bits and then writes it out, optionally it then re-reads the register to validate the change returning true if it worked and false if something went wrong. this makes 3 accesses.

    • Nigel Jones says:

      I don’t do this. There are too many registers that by design don’t read back what you write. For those registers that are supposed to read back what you write then I suppose there is a modicum of value if the write is being performed across an external bus. For internal registers I don’t see any value.

    • Lundin says:

      As input from the the safety-critical applications branch, common practice is to write repeatedly from NVM to the registers. CPU registers are to be regarded as RAM, and therefore shouldn’t be trusted to maintain their values over long periods of time. Modern RAM memories handle refresh of the cells etc automatically, and are very reliable. Yet, you shouldn’t leave the safety of your program in the hands of the RAM memory quality.

      The simplest example of such code is this:

      for(;;) /* main loop */
      {
      SPI_init();
      ATD_init();
      CAN_init();
      /* etc */


      }

      Naturally, reality is often more complex. Some devices you can’t touch once they are set up, there are on-going interrupts etc. The rule of thumb is to write to each register once from -somewhere- in the code.

      As for reading back the values, isn’t as common practice as far as I know. Some safety-critical applications use CRC checks on the whole register area. However, the trend is that MCUs implement such integrity checks on-chip.

      • Nigel Jones says:

        I have seen this technique espoused for safety-critical systems. Do you (or anyone else for that matter) have a research source that you can cite that demonstrates that periodically refreshing registers leads to a safer system? I partially discussed this issue in this post. In a nutshell it seems to me the potential for screwing something up is dramatically higher than the potential for a register randomly losing its value. If the issue is that the code itself is responsible for the ‘corruption’ then it seems to me that ones efforts would be better spent on the underlying problem.

        • Lundin says:

          I don’t know of any research source, but… neither do those fluff standards IEC 60730 and IEC 61508. They encourage all kind of things, walk-pattern tests, March X bla bla bla.

          I recently had to evaluate what technical things an upcoming project needed to take into account for future SIL3 compliance. So I actually dug deep into 61508, and found the actual, plain laughable sources for all these fancy techniques: various newspaper articles, engineering papers (in german…) and hobbyist books from the 70s. They rarely base their rules on actual modern, scientific research! But mainly on ancient out-of-date sources, various engineering legends, and voodoo.

          Regarding walk patterns, the SIL standard has but one single source, an old book from 1986 called “Microcomputers in safety technique” by H.Hölscher. It actually sounds like it could be a book based on scientific research… although MCUs in 1986 were of quite laughable quality compared to the ones we use today. But who knows, maybe the book is still relevant.

  4. Gauthier Fleutot says:

    Not really an issue with the method, rather with the programmers, but in most code I’ve been working on I do not trust the comments so easily. Someone sets values first, write the comments as you do it, then debugs and changes one bit, thinking “I’ll update the comment later”. FAIL!

    Of course this should be caught in reviews, but you know… sometimes it gets through.

    To prevent that from happening, if commenting a value I always add the value itself in the comment. A bit like you do the setup of your sample and hold. For example:

    ADC12CTL0 =
    (0u << 0) | /* 0: Don't start conversion yet */

    I am not surprised anymore, when I see a mismatch between the real value and the value in comment. Easier to spot!

  5. Nigel,

    Speaking of PowerQUICC, I’ve been doing a lot of low-level work with Freescale SoCs lately (MPC8641, MPC8572) and I *have* to use #define’d constants. A lot of registers have so many fields that using your method would be plain illegible.

    To solve the problem, I have written text processing scripts that take the cut’n’pasted register definition from the datasheet PDF and generate the definitions in a header file, with both bit numbers and masks. The scripts also includes the description of each field in comments as well as lists “reserved” fields that you not be touched. These scripts take about an hour to adapt to a new datasheet table format, but they save way more than that, especially when the manufacturer does not provide full headers…

    There is still a problem with that: I have found at least 4 different, still undocumented erratas in the MPC8641 reference manual, some of which affect the bit numbers or addresses of registers ! A lot of times, an experienced embedded programmer will realize something is fishy, but that goes to show that even with “perfect automation” of grabbing data straight from the source can cause errors… From my experience, and probably yours, there are several erratas per reference manual of every chip that go through. I’ve found some in MSP430, PIC, AVR, LPC2xxx chips 🙁

    I like also many of the above commenters’ take on this, especially the “0*” and “1*” to explicitly mention unset / set bits…

    • Nigel Jones says:

      I’m impressed that you can knock out a data sheet parser in an hour. My knowledge of scripting languages is woefully inadequate – and it’s time I rectified the situation.

      • I have made a 5 minute screencast explaining the methodology (http://screencast.com/t/4hKyu13aKP).

        Basically, if you know any scripting language with support for regular expressions (Python, Perl, Ruby, VB and friends, Tcl or whatever), the script you write to generate the header file is generic and a single time expenditure. The part that changes every time is the format of datasheet tables and thus the regular expression to extract bit numbers, descriptions, etc.

        Right now, I’m doing mostly embedded software, but I come from board-level design. I had to learn scripting languages to get the job done (automating connector grids drawing or regular connections, outputting CAM data from CAD files, etc) 😉 I think it’s one of the most time-saving skills I have learned, especially given that most of these languages are simple enough to get comfortable in less than a week of work with a good book.

  6. Lundin says:

    I’m using something similar to Jeff’s #define bit masks version. And I’m too using a homemade parser to process tabbed text obtained from the datasheet pdf.

    This is a lot less work than it sounds! Writing such a parser is a trivial task and you only have to do it once. It will need a script stating the format of the current copied/pasted input text file, such as:

    Once the script for the current file is fixed, you feed the program an input file looking like:

    0x0000 PTAD PTAD7 PTAD6 PTAD5 PTAD4 PTAD3 PTAD2 PTAD1 PTAD0
    0x0001 PTADD PTADD7 PTADD6 PTADD5 PTADD4 PTADD3 PTADD2 PTADD1 PTADD0

    This will yield a .h file looking like this:

    #define PTAD (*(volatile uint8*)0x0000)
    #define PTAD7 0x80
    #define PTAD6 0x40
    #define PTAD5 0x20
    #define PTAD4 0x10
    #define PTAD3 0x08
    #define PTAD2 0x04
    #define PTAD1 0x02
    #define PTAD0 0x01

    #define PTADD (*(volatile uint8*)0x0001)

    Then you can reuse this parser program for all your projects. You’ll need a desktop compiler and fundamental knowledge of the C language. Just download GCC / Dev C++.

    You will still have to read the output manually, as there is indeed a chance for the bit masks to have the same name as other bit masks, or even the same names as registers (especially true for Freescale, it seems).

  7. Hmmm…think I’ll sail against the wind here.

    I have the disadvantage of having come to C from a language where packed bit records were well defined, worked properly whatever size you made them and so on. But since the programming world seems to have standardised on this high-level assembler, I guess I have to work with it 🙂

    I have used the technique extensively since, in C, and the only gotcha I have come across is that in Borland C you can’t span a bitfield across a word boundary. So using the technique on one field in a 32-bit registers in a VGA controller didn’t work well. Other than that they have behaved perfectly.

    I’m sure there are bastard C compilers which I could fall over, but most of the time when I write a project which involves bit-bashing processor registers I only intend that part of the code to work with a single compiler. So I’ma bit sanguine about portability.

    If you DO go down this dark road, the code is SO much easier to read, that, frankly, I’m prepared to be pragmatic.

    My feeling is that if you must program for portability, then I’d define a bunch of symbols for the possible values of each field, and set up a service routine so that you actually write

    setUpAtoD( _5V, single etc etc )

    You might want to define possible enum values for the first field to be 1000,1001,1002 and the second 2000,2002,2003 so the code could check for missing or mispositioned fields.

    David

    • Nigel Jones says:

      I always like a contrarian David.

      • trouble is – in Modula2 – it was so obviously the RIGHT way to do it, and th resulting code was so clear and readable, that I’m prepared to worry a little about portability to retain the readability. It is a lot of work to set up though.

    • Lundin says:

      If you haven’t encountered problems with C’s bit fields, then you cannot have been using them much. 🙂

      The most recent example of bit field disasters I encountered just a few weeks back. There was a seemingly competent programmer who couldn’t get his bus running. I’ve used the same bus a lot myself, and therefore for once knew all about the bits and bytes of all registers. So I read through all of the code, compared with my own drivers etc but couldn’t find any errors. Very mysterious.

      Two weeks later he found the bug, there was a bit in one register which had not been set properly, leaving the bus in a debug mode. The sole cause was register setup through bit fields, where he was tempted to only set up the functionality he was using, rather than setting every single bit in the register through a byte access, as any of the other mentioned register initialization methods above would have enforced.

      I can probably come up with ten or so other examples of bit fields disasters from the past, most of them are indeed related to register mapping fiascos.

      • well hang on a minute – that is a lovely example, but nothing to do with the point.

        A careless programmer can omit to initialise something that needs to be initialised in many ways – I can’t see how that is particularly related to the use of bitfields ?

        All of the other techniques being discussed are horribly vulnerable to mis-definitions, or mis-usage of bit field values, or their use as a shift distance, when they were actually a mask… the list is endless…

        I happily concede that I encountered one issue, where I tried to use a bitfield inside a 32-bit register, which ran over a 16-bit boundary. My tool for checking that doesn’t happen is to put a sizeof check for each bitfield structure. Normally the structure would consist of exactly 8 or 16 bits – so if the compiler has padded unexpectedly, it will fail.

        As I said elsewhere, I mostly know which compiler I’m expecting to use on a project, so once I’m sure it handles bitfields reasonably ( for my purpose ) I’m not too fussed about portability in the short term.

        Can I invite you to come back with an example which actually relates to bitfields as such?

        D

        • Lundin says:

          I’d rather not derail this into yet another bit field discussion. I gave some examples as comments to another article which Nigel referred to above:

          https://embeddedgurus.com/stack-overflow/2009/10/effective-c-tip-6-creating-a-flags-variable/#comment-2390

          It can be summed up as “bit fields are guaranteed to fill your variables with unpredictable values”. “I’m not too fussed about portability” seems like a quite strange statement to me… I believe that would make you the only C programmer in the world who doesn’t re-use code from project to project. 🙂

          Even if you don’t, why would you want to read endless compiler backend documentation about bit field implementations for? If using bit masks instead, you get well-defined behavior and don’t have to spend hours and days reading up on compiler docs for every new project. If there even is a proper documentation, you might be forced to go trial & error, and when you do, there’s always a risk of bugs.

          • thanks for the link – I will read it.

            I’m not going to extend the discussion, provided I’ve put my marker down – I felt Nigel’s dismissal was not a fair discussion.

            My point about portability is – if I’m working on peripheral register definitons for an MSP430 project, as per Nigel’s original example, I’m going to be using a Rowley compiler, coz I just paid $1500 for the pleasure of doing so. I’m not too fussed whether my project compiles identically under another MSP430 compiler. If I swap compilers and IDEs, I’ve got more than bitfields to worry about.

            But you’re right – in the same project I have general-purpose buffer-handling routines, which I would be very distressed if they didn’t work identically under another compiler, horses for courses!

            You’re right, no-one reads ( or believes ) implementation documentation. What you do is examine sceptically the size of the compiled objects ( which of course ought to exactly fill a fixed number of bytes – they are definitions of registers after all -if they exceed the quota, you have trouble ) and do a few simple tests when you first meet a new compiler.

            Now what I will concede is that there is an issue with the available data formats for the fields in the structure – you do need to make sure that they are defined as, and implemented as, unsigned integers. If you stretch it to signed or enum values, then a bit of suck-it-and-see would be indicated.

            My real point is that if I have to do a bit of work at this level to make my actual code read well…. which is surely the point here, I’m prepared to do that on a project basis.

            I’d much rather write

            VGA_horizontal_reg.pixels = 97

            than

            VGA_horizontal_reg = VGA_horizontal_reg & VGA_horizontal_reg_not_pixels_mask
            | ( ( 97 & VGA_horizontal_pixels_mask ) << VGA_horizontal_reg_pixels_shift )

            OK – I know I exaggerate, and we can hide all the workings away in service routines or #defines, but one is readable, the other isn't.

            Really I just wish someone had shot the people who invented C before they had the chance to waste the billions of man-hours and dollars this language has cost us all.

            When I went to uni, the latest research projects were on how to create languages and operating systems, and hardware which whould guarantee reliable operation. If anyone had forecast then that 30 years later, the major piece of softwatre world-wide would be daily invaded by bugs , most of which get in because Microsoft programmers don't know how to avoid writing data past the end of the buffer it is meant to live in, they would have wept. I still do.

        • GroovyD says:

          I use bitfields all the time and never had any problems with them, but then again my compiler of choice is gcc, a good implementation perhaps?

  8. isn’t it interesting – I have just looked at some of my code for handling the AtoD you started with… and I see

    //
    // ENC almost always needs to be off when playing with ADC
    //
    ClrMaskOfBits_port16( ADC12CTL0_16 , ENC ) ;

    //
    // using the internal oscillator, worst case timings for AtoD conversions are:
    // 16 channels 610us
    // 4 channels 150us
    // so one cycle of 16, plus 15 of 4 adds up to 2.86 milliseconds
    //
    ADC12CTL0_16 = ADC12CTL0_hi128_lo128_MSC_2V5_REFON_ADCON_OVintsOFF_u16 ;

    which are sort of interesting!

  9. Dio says:

    Hello Nigel,

    first of all, great article! I enjoyed it very much.

    However, how could one handle a peripheral that has different registers for setting and clearing bits?

    On some CPUs you have to write a ‘1’ to register A to clear bit X and you have to write a ‘1’ to register B to set bit X.

    I find this very error-prone, since you always have to change your code on two spots, if you want to change one bit.

    I’m sorry, if I missed it in the article. It’s been a while since I read it.

    Regards,
    Dio

  10. Tim says:

    I have to leave a comment here because I had an interesting bug happen when trying out this technique:
    const uint32_t CONTROL0_VAL =
    (0u << 0) | //SPI_REG_READ = 0, register write mode (SPI Mode only)
    (0u << 1) | //TM_COUNT_RST = 0, do not suspend the counter
    (0u << 3) | //SW_RESET = 0, do not reset
    (0u << 4) | //RW_CONT = 0, read only one register at a time
    (1u << 6) | //FIFO_EN = 1, FIFO enabled

    writeRegister(RegisterAddress::CONTROL0, CONTROL0_VAL);

    My value was 0x41 instead of 0x40. It turns out I forgot the semicolon, and it OR'd the value with the return from the function. I was surprised this compiled. So you can use a const variable as a parameter to a function that initializes that variable!

Leave a Reply to Nigel Jones

You must be logged in to post a comment.