(Possibly) A simple pointer question - Page 1
Forum section: electronics / general

author: klr5205
author_rank: Regular Contributor
timestamp: June 05, 2017, 05:08:05 pm
content: I'm not much of a programmer, so I was hoping someone could point me in the right direction as far as what's probably going wrong here....If I have this function:Code:void WriteLCDString(char *data){uint8_t i=0;while(data)WriteLCDByte(data[i++]);}Can anybody explain to me whyCode:WriteLCDString((char*) "Test");..doesn't seem to work? For whatever reason it seems like the while(data[ i ]) loop never evaluates as true, and so no data is sent. I have tried making it a do while loop in order to force it to transmit, and it does, but its garbage data.The function WriteLCDByte(uint8_t) works just fine on it own for sending individual characters. My issue is apparently in the use of the pointer. Any ideas?

author: brucehoult
author_rank: Super Contributor
timestamp: June 05, 2017, 05:32:30 pm
content: That's perfectly fine C and works for me on a PC and Raspberry Pi, after adding:Code:#include "stdio.h"#include <stdint.h>void WriteLCDByte(char b){putchar(b);}However that cast to (char*) is a bad idea. What you should be doing is void "WriteLCDString(const char *data)"

author: TNorthover
author_rank: Contributor
timestamp: June 05, 2017, 05:38:40 pm
content: There's nothing intrinsically wrong with that.What platform are you using and how are you producing your binary? If you're using hand-written linker scripts or some equivalent it'd be semi-easy to accidentally leave the string out of the final binary. Then the CPU might read 0s from where it expects it to be.

author: klr5205
author_rank: Regular Contributor
timestamp: June 05, 2017, 07:27:34 pm
content: I'm using MPLABX v3.60 for my IDE and the XC8 compiler and linker.This one has me fairly stumped at the moment... must be some "trap for young players" I'm overlooking!

author: newbrain
author_rank: Super Contributor
timestamp: June 06, 2017, 09:29:49 am
content: Quote from: brucehoult on June 05, 2017, 05:32:30 pmWith a C standard compliant compiler, the cast would just be redundant (not good style, though).A string literal decays to type char* in this context; from the C99 standard, 6.4.5 String Literals:QuoteOf course, albeit a char*, modifying the contents is not OK:QuoteIn C++ instead, string literals are actually arrays of const char: trap for young and old players...BUTfrom the XC8 user manual we learn (5.4.6.3):QuoteSaying that the type of string constant is const char * isplain wrong: it's an array, and it will yield a pointer unless used as argument of & or sizeof, or used to initialize another array!And the PICs (AFAICS, I'm definitely not into them...) have different type of pointers for code (where const are stored) and data memory.The compiler should be able to sort it out by itself and do the magic without the user noticing, but maybe in this case (possibly due to the cast) it's not.tl,dr: Probably brucehoult is psychic, as he gave good advice before knowing XC8 vagaries were involved...

author: mikerj
author_rank: Super Contributor
timestamp: June 06, 2017, 10:13:39 am
content: Quote from: newbrain on June 06, 2017, 09:29:49 amThe XC8 compiler puts const data into program memory, so a literal string must be referenced via a pointer of type const char * (or const x[]).It's been a long time since I used this compiler (it was owned by HiTech back then) but IIRC a pointer to const char* can access data in either program or data memory, but a pointer of type char * can only access data memory.

author: brucehoult
author_rank: Super Contributor
timestamp: June 06, 2017, 10:24:02 am
content: Quote from: newbrain on June 06, 2017, 09:29:49 amNice. I haven't come across XC8 before. "char*" and "const char*" refer to different address spaces! Ouch. On AVR you tend to have to explicitly put __progmem__ explicitly on a variable to have it kept read-only in flash. Otherwise it's automagically copied into your (usually much smaller) RAM on program startup.On my HiFive1 board code executes from flash, and there is an instruction cache but not a data cache. So if you accidentally have data loads from program space in your inner loop, instead of copying it to SRAM at startup (as happens automatically if the compiler thinks it's not const), it works but is 100x slower.It's certainly hard to give good advice when people don't state what platform they're using! But type safety and a minimum of explicit casting is always a good idea.

author: klr5205
author_rank: Regular Contributor
timestamp: June 06, 2017, 01:10:44 pm
content: Hmm, I tried initializing an array and passing it to WriteLCDString like so:Code:const char word[] = {'T','e','s','t'};WriteLCDString(*word);And it will print out the same nonsense as when I had forced it to print with the do while loop before. What's interesting to me is the fact that it doesn't seem to matter what the array contains, and regardless of its size, it always prints out the same character (looks like a greek alpha) followed by 4 blanks. Can that be any sort of a clue?

author: IanB
author_rank: Super Contributor
timestamp: June 06, 2017, 01:16:58 pm
content: What happens if you try the following?Code:const char word[] = {'T','e','s','t'};WriteLCDString(word);

author: IanB
author_rank: Super Contributor
timestamp: June 06, 2017, 01:21:45 pm
content: Quote from: klr5205 on June 05, 2017, 05:08:05 pmI don't know why it doesn't work, but this would be better style. Does it work any differently?Code:void WriteLCDString(char *data){while(*data)WriteLCDByte(*data++);}Code:WriteLCDString("Test");

author: klr5205
author_rank: Regular Contributor
timestamp: June 06, 2017, 01:33:10 pm
content: In both of those examples the result is the same, which is to say that nothing is printed.Is there anything useful I can do from a debug standpoint?

author: JPortici
author_rank: Super Contributor
timestamp: June 06, 2017, 02:02:41 pm
content: if you single-step and add "data" to the watch window... what do you see after you enter the function?Code:WriteLCDString((char*) "Test");it's not much help but this kind of prototype and this kind of call (but without the explicit cast) is working under XC16at the very least you should see "T" or the address where "T" is.. and in the memory window you should see "T" at the address

author: brucehoult
author_rank: Super Contributor
timestamp: June 06, 2017, 02:28:59 pm
content: Quote from: klr5205 on June 06, 2017, 01:10:44 pmI don't even know how that compiles, since WriteLCDString wants a char* and you're now giving it a char!Definitely get rid of the *. And based on earlier discussion I'd try getting rid of the const too, unless perhaps WriteLCDString is declared to take a const. In which case, try getting rid of *both* consts as well.

author: rstofer
author_rank: Super Contributor
timestamp: June 06, 2017, 02:31:41 pm
content: I would add a break to WriteLCDByte() and see what is coming in. Is it a proven fact that WriteLCDByte() works?As an aside, I don't always have a practical debugger and I really don't want to use printf() if the library brings along a heap requirement. So, for every uC project, the first thing I do is get the serial port working. Then I copy the various string/conversion functions from "The C Programming Language". Once I can send strings and hexified bytes, shorts and ints, I can get down to the real work.

author: newbrain
author_rank: Super Contributor
timestamp: June 06, 2017, 02:44:50 pm
content: Quote from: klr5205 on June 06, 2017, 01:10:44 pmWell, this code contains two faults:When one initializes a string in that way, the terminating zero valued character cannot be omitted:Code:const char word[] = {'T','e','s','t','\0'};The function expects a pointer to char (or a pointer to const char?) but in the code*wordis used:word, being an array, decays to a pointer and the * operator takes the content of object pointed to. In this case, 'T' is passed.(Ninjaed by brucehoult...)The compiler should vigorously complain when it sees a single char instead of a pointer, though, unless there's an explicit cast (don't do that)Are you perchance using aPIC18F24/..K40?Search NVMREG in the errata linked above, and seeherefor a workaround.Unfortunately I can't help much more, as I just have google but no PICs...Edit: clarity.

author: klr5205
author_rank: Regular Contributor
timestamp: June 06, 2017, 03:05:15 pm
content: Quote from: rstofer on June 06, 2017, 02:31:41 pmYes I can write messages on the LCD one letter a time using WriteLCDByte(); it is working as expected.If I single step through the program, once the program enters WriteLCDString(), *data = 'T'; 0x54However, it exits the function without ever going to WriteLCDByte(), meaning while(data) was false for some reason, which doesn't make sense to me

author: klr5205
author_rank: Regular Contributor
timestamp: June 06, 2017, 03:15:42 pm
content: Quote from: newbrain on June 06, 2017, 02:44:50 pmI'm using a PIC18F27K40, however that section of the errata states that all PIC18F__K40 devices suffer from the issue they describe. That may well be it; I'll report back once I have a chance to test.

author: JPortici
author_rank: Super Contributor
timestamp: June 06, 2017, 04:28:50 pm
content: Solved in A4 silicon revision. you know which is it when the programmer read the device IDi again suggest you single step with the debuggeralso, simulator should be able to run the code so one doesn't need the physical chip to test (and testing should be faster too)

author: klr5205
author_rank: Regular Contributor
timestamp: June 06, 2017, 08:19:25 pm
content: That was it! My device is revision A2 so the workaround described in the link above was applicable.Thanks to everyone especially newbrain for your help!

author: newbrain
author_rank: Super Contributor
timestamp: June 06, 2017, 08:38:19 pm
content: You are welcome!Glad to see it solved.Always check for errata.I was, e.g., bitten by this bug inSTM32F746:"Missed ADC triggers from TIM1/TIM8, TIM2/TIM5/TIM4/TIM6/TRGO or TGRO2 event"and lost some hours before doing my homework and enabling the DAC clock.

author: brucehoult
author_rank: Super Contributor
timestamp: June 07, 2017, 09:29:24 am
content: Quote from: newbrain on June 06, 2017, 02:44:50 pmUgh.So, as I suggested, it was a problem reading data from program memory, and (as I also suggested several times) getting rid of the "const" on the declaration of "word" should also have worked around the problem.Well, assuming the startup code in the runtime library that copies writable initialised data from ROM to RAM was not similarly affected by the chip errata, which may or may not be the case.

author: newbrain
author_rank: Super Contributor
timestamp: June 07, 2017, 10:26:33 am
content: Quote from: brucehoult on June 07, 2017, 09:29:24 amEh..probably not, the startup code and the compiled one apparently use a table access instruction that does not work without the workaround.So there's almost no sane way to initialize a string, as the compiler usesconst charfor string literals (against the standard), apart from assigning one char element at the time.At least, that's my understanding, but I might very well be wrong

author: NivagSwerdna
author_rank: Super Contributor
timestamp: June 07, 2017, 11:23:42 am
content: Quote from: newbrain on June 06, 2017, 02:44:50 pmOOooo.. that's brutal. Very useful info newbrain.