Showing posts with label Microchip. Show all posts
Showing posts with label Microchip. Show all posts

20140615

Do you see too?

Here's another gripe about the 'C' programming language or at least Microchip's C18 implementation of it. Oh, and by the way, here's another programmer's take on the subject.

Here a quote from the C18 user guide:

2.7.1 Integer Promotions
ISO mandates that all arithmetic be performed at int precision or greater. By default, MPLAB C18 will perform arithmetic at the size of the largest operand, even if both operands are smaller than an int. The ISO mandated behavior can be instated via the -Oi command-line option.

When coding for an 8-bit microprocessor one tends to use 8-bit (byte) variables wherever possible as these are native to the processor.  If you are coding in C18 you are likely to be using one of the PIC18 range of processors and these, we know, have a hardware 8-bit by 8-bit multiplier giving a 16-bit result in one machine cycle.

unsigned char a, b;
unsigned short int c;
a = 100;
b = 200;
c = a * b;

How crazy is it, then, that the above code in C18 will yield 'c' = 32!  the last line will of course use the hardware multiplier and then will throw away the upper byte of the result before expanding the answer to 16-bits by adding a zero upper byte and placing it in variable 'c'.

Worse still is

#define factor 31
unsigned char a;
a = ((factor * 60) / 100);

Here the intention is to give variable 'a' the value 60% of that of constant 'factor'. The constant expression to the right of the equals sign is, of course, evaluated at compilation time so that the run-time code sees simply 'a' being set to a constant.  In assembler I am used such expressions being evaluated without loss of precision until the answer is placed into 'a' and raising a warning if it is too big. But in C18 the expression is evaluated in byte precision and yielding 'a' = 0. To stop this happening one can use:

a = ((factor * 60ul) / 100ul);
to force the evaluation to use unsigned long (32-bit) precision, but this is hardly intuitive.  I would not have so much minded had C18 deigned to raise a warning but, no, it blithely continues in its legalistic, archaic and ridiculous way...

The reason for using 'C' for embedded processors is to increase productivity and yet produce tight, efficient code. The above example shows how 'C' fails to do this as, I think, the only way to force the compiler to do the obvious is to insert assembler code.

20140608

Do you see?

In addition to assembler I use the ANSI-C under the MPLAB programming environment for writing code for embedded microprocessors in my work. Whilst 'C' enables me to write code more quickly and is more maintainable than assembler, I contend that the claim that 'C' makes code that is almost as tight and efficient as assembler false. I contend that the 'C' language is unnecessarily convoluted and far from elegant.

For example in an recent project I used the PIC18F67K22, a 8-bitter with a massive 128K of code memory, for a reasonably simple task. In assembler I would have expected my code to fit into less than 32K of memory, but for this project I used 'C' and it filled up about 80K of code.

'C' header files can be hard to find because their path is often implicit, hidden in the compiler configuration. I say that the that MPLAB ought to provide a more intuitive way to find header files.

The 'C' "#define" and user type definition statements are very powerful but lends themselves to hopelessly nested definitions, and an attempt to find out what a label actually means can lead to a long rabbit trail through multiple source and header files that can end in the compiler's own source files. There ought to be a way to display this rabbit trail as a list rather than have to follow it.

I contend that variables in 'C' are too "strongly typed". This often results in the need to prefix a variable passed to an inbuilt function with a non-intuitive cast to stop the compiler generating an error. Which detracts from the whole point of casting and implies another contention - casting is a way to change a variable from one type to another but it is not always clear what information is lost is so doing.  Here is an example from my code: to simplify the main code I have defined a new type "PGMCHAR" and used this rather than the original "const rom char far" to cast a literal string in order to satisfy the use of the inbuilt function "strcpypgm2ram":

typedef const rom char far  PGMCHAR;
strcpypgm2ram(wbuffer, (PGMCHAR*) "Hello world");

Which in any decent sort of language this would read:
wbuffer = "Hello worldd";

I contend that a language like 'C' which claims to give low-level access ought to allow the programmer to do anything that is safe. And yet 'C' does nothing to stop you corrupting memory outside your remit, but does many things to restrict your freedom such as insisting that strings are signed and have to be terminated by a zero. Thus making it impossible to have a string with zero in it, and difficult to use the upper 128 character codes.

IMHO a programming language ought to be intuitive, as close to assembler as is reasonable and with a plethora of simply styled functions and constructs. I have often mused about writing such a language. But it would never be adopted because there is a professional pride thing that makes programmers adhere to 'C' and its many derivatives.

20140527

Dyslexia

Perhaps I have a twinge of numerical dyslexia. In my work I order stuff online from Farnell who use six or seven digit order codes. I can think I have committed such a code to short-term memory then type it into my order and find I have the digits in the wrong order. Often I know I am writing it wrong, but cannot be sure what the right way is. This happens so frequently that generally I have resorted to writing down even the simplest of codes. It is the same with telephone numbers.


SOT23-5 semiconductor package
Microchip make amplifier chips and specialise in lower power rail-to-rail input and output types, and these I find useful in my work. Recently I chose the MCP6401 in SOT23-5 package from the many to choose from.  So I ended up ordering the MCP6041 by mistake, and after re-ordering got mixed up between the MCP6401 and MCP6041R.



Whilst similar there are enough differences between MCP6401 and MCP6041 to matter, and you can see that MCP6401R has a different pin-out. So I decided to purge my stocks and only keep the MCP6041R for which I have a symbol in my PCB-CAD software. In the design I am currently working on I used this symbol only to find I had mixed up the two pins VIN+ and VIN-.  Duhhh...

My next catastrophe was with LED's. I needed a high-brightness red indicator LED and chose one by Cree in a PLCC-4 package. You'll see from my picture that three of the legs all connect to the cathode of the LED, and one to the anode. And there is a diagonal line "cathode marking".

PLCC-4 LED package
In a subsequent project I wanted high-brightness LED's in different colours so manfully chose a bunch from the Farnell online store only to find, after designing my PCB and soldering them in, that they didn't work because they use the four pins differently. I had naively assumed all PLCC-4 LED's would use the same pin-out - indeed, why-ever not?

There's a moral somewhere...

20131226

F8 bug or how I hate I2C bus

I have been revisiting assembler code I wrote maybe 10 years ago because of a software bug.  The device announces the problem by displaying a message "ERROR CODE F8" which, being translated into the vernacular, means that when my Microchip 8-bit microprocessor accesses external serial EEPROM via I2C bus the bus gets into a can't-get-out-of-it situation. That the serial memory devices should suffer such ignominy is my case against I2C.  SPI bus is simpler and is my preferred choice.

It turned out I was trying to access a memory address that did not exist and, after many days of searching, it turned out that this was because I had initialised said memory (pointers and wotnot) after my first attempt to access it. The solution - move the call to the initialise code back before the first access. Simple - once I had identified the problem.

It took me so long to identify because I had very poor debug tools.  I had very poor debug tools because I hadn't previously made any those 10 years back when my teeth were shorter, and in this recent spate of work I thought I could do it without the necessary tools. This morning I decided that was bad thinking. I made a terminal routine that would dump salient registers to a display screen and hey presto I was able to locate the bug. The most helpful part of my debug tool was the display of stack pointer and return addresses on the stack from which I was able to figure what part of the code caused the error display.

Which adds weight against the inference in the absurd proverb "a bad workman blames his tools". It should read something more like "a bad workman has bad tools".

A similar principle applies when routing cables through or driving screws or hammering nails in awkward positions. If you cannot see what you are doing, chances are you will mess up: the screw will drop into a void, you will instead hit the nail on your finger, or the cable being poked will just refuse to exit where you want. But once you can see what you are up against it is oh so much easier!  For this reason I keep a stock of small flash-lights and mirrors.

There could be a moral here somewhere...