C Code Problem - Page 1
Forum section: electronics / general

author: Digibin
author_rank: Regular Contributor
timestamp: January 12, 2015, 09:56:05 pm
content: HeyI've got an issue with some C code I'm working on. It's for a bat detector project. It measures the frequency of a LO and displays this on a LCD screen. It also polls a rotary encoder and displays a counter that goes up and down with movement of the encoder. It also talks to two 5k digital potentiometers that are hooked up in series. Changing the pots causes the LO to vary from approx 15kHz to 100kHz. The idea is to have everything integrated so that the encoder controls the LO frequency and this is measured and displayed to the user.Before integrating it together I wanted to make the frequency control more linear. The LO is way more sensitive to each step change in potentiometer at the higher frequencies. So I took some measurements and created an array in my code that stores the values that I want the potentiometer to cycle through in order to get linear frequency control. This is when my code broke...I've attached the code, main() is in Bat Detector v2. The problem area is right at the bottom of main(). The code, as attached, compiles with no warnings but instead of seeing the measured frequency on the display it gives a series of random characters/symbols. The problem to the best of my knowledge is related to the line:unsigned int digipot_value = digipot_linear_values;So this is meant to the element in the array that corresponds to 'enc_count' and copy it to 'digipot_value'. It works just fine with this line, but if 'digipot_value' is then used in either of the following functions sprintf() or update_digipot(), the LCD displays rubbish. If I comment out those two functions it works fine. With either of them in, it breaks. Both functions work fine if another variable is passed or if a straight integer is passed. It's only the digipot_value variable that's causing trouble. So I suspect something is going on with respect to the array I'm using.Somehow the strings that I pass to the lcd_puts() function are being messed with, resulting in jibberish on the screen. But even the string 'char_freq_Hz_avg' is messed up even though it seemingly has no connection to the code in the problem area. This leads me to think that somehow the memory is being messed up.I'm not really familiar with pointers (haven't worked with them much) but this may be related to how C deals with arrays as pointers to the first memory address, perhaps?Been struggling with this one for a while. Any ideas?Cheers.

author: IanB
author_rank: Super Contributor
timestamp: January 12, 2015, 10:11:22 pm
content: It seems you keep adding "result" onto "enc_count" without ever resetting enc_count. Is this really what you intend?

author: jav
author_rank: Contributor
timestamp: January 12, 2015, 10:49:28 pm
content: Also, you should make sure that len > 2 (from len = strlen(char_freq_Hz_avg)before continuing or really bad things may happen when you try to modify char_freq_Hz_avg[len-3].

author: dgtl
author_rank: Regular Contributor
timestamp: January 12, 2015, 11:46:06 pm
content: Stack overflow? You are using quite a lot of large variables; AVRs do not have too much memory.Just some ideas to optimize the code:Declare constant (non-changeable) arrays as const. The digipot_linear_values array will otherwise be copied to memory at start; instead it should be accessed from flash as needed. Flash is not modifiable (at least for C code), thus everiything non-const goes to ram.The digipot_linear_values array could actually be specified as sparse array. For example, you store only every 2nd or fourth value; then perform linear approximation for values inbetween. Using power of two (2,4,8,...) as the step of the array makes calculations much faster. For longer arrays, the space savings may be quite large and the added code space taken by the calculation could be less.Also, you should make static the functions and global variables that are not used outside that compilation object (outside that c file). This way the compiler may find better ways to optimize the code if it does not have to keep the standard interface (to be compatible in case maybe somebody calls from outside). Then you can make more smaller functions; for example move the lcd output of frequency and lcd ouput of digipot value to separate functions, that just take value as argument. it is much more readable to write update_digipot(value); update_lcd_digipot(value);. I like the code style that every function must fit fully on screen; if you need to scroll, it is too long.freq_Hz_avg divide by three rounds always down. Maybe use 4 samples and add value of 2 before dividing by 4?The main and TIMER0_OVF_vect are not synchronized properly? freq_Hz can be modified from ISR at the same time it is read from main (reading multi-byte data from ram is not atomic, some bytes may change during the read)? Also, you are not waiting until measurement is complete, so you may add the same value twice etc. The IRQ handler might fire for example after evaluating switch condition, so you'll use new result with old switch condition. One way would be to move the closing brace from line 170 to line 208 and the init_freq_measurement also to line 208. So you handle the result only if it is ready and then start another.You do not have to stop the timers when taking result reading. Just read the TCNT value as the first thing in the IRQ handler and substract the last value (that you got the last IRQ handler run). Then store the TCNT value you read out to a variable for using it next time. Just disable the irqs while reading the volatile global variables to local variables to get consistent state, then re-enable.Instead of 3-measurement average, you could also use a simple low-pass filter, like exponential moving average. For example, after every measurement, output=(1/4)*input+(3/4)*last_output. This can be optimized: sum = sum - output + input; output = sum/4; (store both sum and output vars). This could be also calculated in IRQ handler as it is fast enough. Then you have only one global variable shared between irq and main, the filtered output frequency itself. As it is multi-byte, still disable irqs while reading it out to a local var in main.How precise the freq measurement has to be? If some kind of interrupt fires and wastes time at init_freq_measurement between lines 59/60, your timings and result would be off. If it is important, disable IRQs during those 2 lines. The same happens when something delays timer0 ovf irq. If you have spare pins, you could connect timer0 output compare output pin to timer1 input capture pin and handle the timer1 input capture IRQ. Then capturing is done on every cycle of timer0 by hardware and you just post-process the result. You get single-clock-cycle precision.sprintf is large and slow. If it is the only use of it you may consider replacing it. Even divide-output-divide-output unrolled cycle would be smaller for uint16.enc_count may go out of bounds (line216). C does not check array bounds. Check it yourself, do not allow the value to go outside of bounds. Also, enc_count may be uint8_t?Hope this helps. Maybe the real problem is somewhere else, but these are the things that I happened to notice.

author: dannyf
author_rank: Super Contributor
timestamp: January 13, 2015, 12:24:06 am
content: I just went through your code quickly.You did a good job modularizing and commenting the code. NO obvious problems jumping out at me.I would suggest that you try to isolate the bug by passing given values to the various code pieces and see if they produce the desired behaviors. For example, if you suspect that the lcd_puts() isn't doing its job right, initialize a string and pass it to lcd_puts() and see what happens.

author: dannyf
author_rank: Super Contributor
timestamp: January 13, 2015, 12:43:21 am
content: lcd_puts() is fine.The issue is with the conversion of freq_Hz_avg to a string, when freq_Hz_avg is less than 1000.You used a lot of string functions, particularly sprintf(). They are quite expensive in terms of space + time. Try to think something else.In the mean time, you can replace the whole section with a one-liner:Code:sprintf(char_freq_Hz_avg, "%lu.%lu kHz ", freq_Hz_avg/1000, (freq_Hz_avg % 1000) / 100);

author: Howardlong
author_rank: Super Contributor
timestamp: January 13, 2015, 10:46:04 am
content: I would agree that the sprintf probably needs looking at. It is both flash and RAM hungry.I don't know the sprintf implementation on the device you're using, but if I am reading the device's spec sheet right you only have 256 or 512 bytes of RAM available on an attiny48. Quite frequently sprintf implementations use heap, and some implementations automatically require floating point primitives, but either way it will be using a lot of RAM. With only 4/8k flash and 256/512 bytes RAM I'd look at less generic methods of generating ASCII.If that sprintf fails, then you do that strlen on the resulting string, the results are unlikely to be very nice.

author: Digibin
author_rank: Regular Contributor
timestamp: January 13, 2015, 09:32:05 pm
content: Quote from: IanB on January 12, 2015, 10:11:22 pmYes - 'result' can only be -1, 0, or +1. So 'enc_count' just follows CW/CCW turns of the encoder. However I agree that it should be bounded manually as pointed out later.Quote from: jav on January 12, 2015, 10:49:28 pmGood point. In this case would it go the memory address one prior to the start of the array (whatever may be there) and operate on that location?Quote from: dgtl on January 12, 2015, 11:46:06 pmReally useful post, thanks very much. Your tips make a lot of sense and I will implement them before further developing the code.Quote from: dannyf on January 13, 2015, 12:43:21 amYep that's definitely a more elegant way of doing it. The only reason I'm using sprintf()is because I need to convert integers to strings for the lcd_puts() function. Previously I was using itoa() but moved to sprintf() because it seems more formal; it's an implicit function of C (to my understanding anyway). What are the alternatives to sprintf() in my case?What do you mean specifically by 'when freq_Hz_avg is less than 1000'?Quote from: Howardlong on January 13, 2015, 10:46:04 amWhen I build the code in AVR Studio it gives me:Program Memory Usage : 3286 bytes 80.2% FullData Memory Usage : 195 bytes 76.2% FullSo presumably it's not a lack of memory. Having said that, how would you implement integer to character conversion more efficiently?

author: senso
author_rank: Frequent Contributor
timestamp: January 13, 2015, 10:47:11 pm
content: If you are using all that RAM at compile time, its sure that you are blowing up the stack when that sprintf function gets called.Use a micro with more ram or change the code.

author: jav
author_rank: Contributor
timestamp: January 13, 2015, 10:47:39 pm
content: Quote from: graymp89 on January 13, 2015, 09:32:05 pmIt'll depend on the alignment of the array. If it isn't at the beginning of a block, it'll probably overwrite the previous addresses. It may also "wrap around" to higher addresses and overwrite those.When freq_Hz_avg < 100, len will be < 3 and you'll run into that problem.If you only need to convert integers it's more efficient in terms of code size to write your own itoa function. You can write it specifically for the format you need.For instance:Code:void itokhz(unsigned int freq, unsigned char *buffer) {int started = 0;if (freq >= 10000) {*buffer++ = '0' | (freq / 10000);freq %= 10000;started = 1;}if (started || freq >= 1000) {*buffer++ = '0' | (freq / 1000);freq %= 1000;} else*buffer++ = '0';*buffer++ = '.';if (freq >= 100) {*buffer++ = '0' | (freq / 100);freq %= 100;}if (freq >= 10) {*buffer++ = '0' | (freq / 10);freq %= 10;}*buffer++ = '0' | freq;strcpy(buffer, " kHz");}You can even optimize it further depending of the cost of divisions in your architecture or statically allocating the output buffer within the function if you don't require reentrancy.

author: dannyf
author_rank: Super Contributor
timestamp: January 13, 2015, 10:50:28 pm
content: Quoteitoa(), either the stock version or your own roll -> which can be quickly implemented using a look-up table.

author: Sal Ammoniac
author_rank: Super Contributor
timestamp: January 14, 2015, 07:25:01 am
content: Is this a commercial project (and, hence, cost sensitive), or a one-off?If it's a one-off, why bother using a memory constricted MCU like an ATTiny when for just a little bit more you can use an ARM Cortex-M0 or M3 that won't have the tight memory issues?

author: Jeroen3
author_rank: Super Contributor
timestamp: January 14, 2015, 08:04:30 am
content: Quote from: senso on January 13, 2015, 10:47:11 pmIncrease the stack. Generate a callgraph to know the size.Every function call uses stack. This stack consists of the cpu registers + local variables. And you must always have enough free stack to fit your biggest interrupt routine. Again, check the callgraph to know the stack of each routine.I once tried C on an Attiny10. Not a single function call is possible in that tiny memory. Its called tiny for a reason.And use snprintf if you can.

author: Howardlong
author_rank: Super Contributor
timestamp: January 14, 2015, 08:15:37 am
content: Quote from: Sal Ammoniac on January 14, 2015, 07:25:01 am... and the small matter of learning a whole new development paradigm, getting the toolchains to work, etc... and six weeks later, maybe have enough to be productive. Just sayin'

author: dannyf
author_rank: Super Contributor
timestamp: January 14, 2015, 11:58:16 am
content: QuoteThe existing code fits an Attiny. I would use a mega if I already have the chip. It is crazy to go to a CM0/3 chip for that.printf(), depending on implementation, takes about 2kb of space. So if you are tight on space, thinking about taking it out.One way to go is something like this:Code:strcpy(char_freq_Hz_avg, str1); //copy string into lcd bufferfreq_Hz_avg /= 100; //get to the hundredschar_freq_Hz_avg=(freq_Hz_avg % 10) + '0'; freq_Hz_avg /= 10; //convert the hundredschar_freq_Hz_avg='.';char_freq_Hz_avg=(freq_Hz_avg % 10) + '0'; freq_Hz_avg /= 10; //convert the thousandschar_freq_Hz_avg=(freq_Hz_avg % 10) + '0'; freq_Hz_avg /= 10; //convert the ten thousands...//if you don't want leading zero, use this insteadif (freq_Hz_avg) char_freq_Hz_avg=(freq_Hz_avg % 10) + '0'; freq_Hz_avg /= 10; //convert the ten thousandsDo the same with other sprintf() invocations and you should save quite a lot of space.

author: Sal Ammoniac
author_rank: Super Contributor
timestamp: January 14, 2015, 07:15:31 pm
content: Quote from: Howardlong on January 14, 2015, 08:15:37 amIt typically takes me about three days to get up to speed on a new processor/toolchain to the point where I'm productive writing C code for that platform. I guess I assumed everyone can do that. I guess I thought wrong.

author: Howardlong
author_rank: Super Contributor
timestamp: January 14, 2015, 09:44:17 pm
content: In which case you are super clever!Does that include the time spent figuring out the difference between the gazillions of offerings out there, and evaluating which one would work for your requirement?Either way, to be productive, ie realistically writing production code, for a new platform in three days froms scratch, is a very big ask indeed.Fannying about trying to understand the USB stack or something, yes, three days, but you wouldn't be writing production code at that point but maybe I'm just a thicko!

author: Sal Ammoniac
author_rank: Super Contributor
timestamp: January 15, 2015, 01:24:35 am
content: Quote from: Howardlong on January 14, 2015, 09:44:17 pmIt only took me a week to port an RTOS from ARM Cortex-M3 to PIC32. I had never used any of the PIC tools before, nor did I have any experience with MIPS before I started. Where there's a will, there's a way.I'm not saying any of this to brag--just that learning a new processor and/or environment isn't that hard and needn't take very long for an experienced person. I have 30+ years experience--perhaps that's why it doesn't take me long to get up to speed on something new.

author: Digibin
author_rank: Regular Contributor
timestamp: January 15, 2015, 09:57:10 pm
content: Okay I've gone through the code and cleaned it up based on the various comments. Thanks for all the advice. However the problem still persists.I've not moved to an alternative to sprintf() yet but I will do soon (limited time in the evenings to work on this). It sounds like sprintf() may be an issue but I'm not seeing exactly why simply using this function causes the strings in memory to be corrupted? I have enough memory, according to the Program Memory Usage and Data Memory Usage reported by the compiler, yes?Either way I will try an alternative.Quote from: jav on January 13, 2015, 10:47:39 pmQuote from: dannyf on January 14, 2015, 11:58:16 amOkay took me a while to realise that in both these cases you're actually manually setting the ASCII code of each char according to each digit in the integer. Didn't get this until I looked up the ASCII code of zero. In the second example, what is the purpose of the strcpy() line? Does 'str1' need to be initialised to something specific or...?

author: AndreasF
author_rank: Frequent Contributor
timestamp: January 16, 2015, 09:36:16 am
content: I agree with the others that it's worthwhile going away from sprintf. There is the potential memory use/corruption issue, and given that your requirements of converting your measurements from int to char[] are very predictable, you don't really need the flexibility of sprintf. I've used similar approaches to the ones already given in the past. I'v even simplified it further by not using divisions or modulus operations:Code:char temp = '0';char started = 0;while (freq>=10000) {temp++;freq-=10000;started = 1;}if (started) {LCD_putChar(temp); //or whatever the actual function is to send a single char} else {LCD_putChar(' ');}temp = '0';while (freq>=1000) {temp++;freq-=1000;}LCD_putChar(temp);LCD_putChar('.')temp = '0';while (freq>=100) {temp++;freq-=100;}LCD_putChar(temp);temp = '0';while (freq>=10) {temp++;freq-=10;}LCD_putChar(temp);As you can see, this also gets rid of the buffer. Since each decimal place is determined individually, you can directly write it to the LCD instead of into a string buffer. That would also avoid having to write " kHz" every single time. It only needs to be written once (or after every LCD reset/clear) and should just stay on the display from then on.To further avoid divisions, you may want to consider taking four samples instead of 3 (and why not summing them directly?), and then dividing by 4, which can be achieved with a simple bitshift by 2.Lastly, how confident are you that the LCD functions don't also use your timer?QuoteDoes your string buffer end with 0 as a proper 'end-of-string' marker? Otherwise lcd_puts may simple keep sending random memory content to the LCD (until it hits a 0 byte).

author: Digibin
author_rank: Regular Contributor
timestamp: January 16, 2015, 12:51:59 pm
content: Quote from: AndreasF on January 16, 2015, 09:36:16 amThanks for the code, I'll give this a go. I've been trying the other examples too but with no luck - don't understand why so I'll post more details on that later.What buffer are you referring to? The one mentioned in the example below?Quote from: jav on January 13, 2015, 10:47:39 pmIf so I'm not sure I understand it's purpose to begin with! My understanding is that the function expects a dereferenced pointer to a char variable (i.e. the value stored in the location it's pointing to). So does this mean I have to actually pass it a pointer variable that's initialised to point to my string, or can I just pass it the string directly? i.e.itokhz(freq_Hz_avg, *my_pointer); (where 'my_pointer' points to &char_freq_Hz_avg)oritokhz(freq_Hz_avg, char_freq_Hz_avg);Quote from: AndreasF on January 16, 2015, 09:36:16 amWhy is avoiding divisions desirable? Purely to minimise computational effort, or for other reasons too?Quote from: AndreasF on January 16, 2015, 09:36:16 amPretty sure - it uses software delays for its timing. I've not had a problem with the LCD with any of the code I originally postedexceptfor when I tried to use the array. Without the array, sprintf() works just fine and the code behaves as I intended. So I'm still not seeing where the actual problem is, I'm sure it's related to the integer array.Quote from: AndreasF on January 16, 2015, 09:36:16 amAgain not sure what you mean by buffer - my string is my string, it's not buffering anything that I'm aware of. I write to it, and push it to the LCD. But again this all works fine in the absence of the array-related code. So I can't help but suspect that these lines are somehow affecting the strings, but can't see how they might be doing so as they don't involve the string variables.

author: sleemanj
author_rank: Super Contributor
timestamp: January 16, 2015, 01:24:40 pm
content: Quote from: graymp89 on January 15, 2015, 09:57:10 pmNo.You mistake that report for "this is all the memory you will ever use", which it is not. It is how much memory is used to begin with.Every time you call a function, a stack frame is allocated to cater for the local variables used within that function call, this does not form part of the linked size, it's a runtime allocation. When the function exits, the stack frame is released. Additionally functions may at run time dynamically allocate memory from the heap.Neither stack growth, nor heap growth can be known at compile time because the compiler, does not know in what exact manner your program will run, what functions it will call, how "deep" it will go, and how much heap will be consumed at any given time, because this is all determined by your program behaviour, inputs etc. And therefore, it could not know that, without running the program, with infinite combinations of inputs, which is of course, impossible.

author: Digibin
author_rank: Regular Contributor
timestamp: January 16, 2015, 01:35:02 pm
content: I've tried creating a function based on danny's suggested code:Code:static void itokhz(unsigned int freq, char char_freq[]){char_freq= '\0';// ensure string is properly terminatedfreq /= 100;// get to the hundredschar_freq=(freq % 10) + '0';// convert the hundreds to ASCII charfreq /= 10;// get to the thousandschar_freq='.';// insert decimal pointchar_freq=(freq % 10) + '0';// convert the thousands to ASCII charfreq /= 10;// get to the ten thousandsif (freq)// if the ten thousands is non-zero{char_freq=(freq % 10) + '0';// convert the ten thousandsfreq /= 10;// get to the hundred thousands}if (freq)// if the hundred thousands is non-zero{char_freq=(freq % 10) + '0';// convert the hundred thousands to ASCII charfreq /= 10;}if (freq)// if the millions is non-zero{char_freq=(freq % 10) + '0';// convert the millions to ASCII charfreq /= 10;}if (freq)// if the tens of millions is non-zero{char_freq=(freq % 10) + '0';// convert the tens of millions to ASCII charfreq /= 10;}}I've attached the full code (attempt1.c). Note that I've commented out the code that was causing my original issue so I can focus on replacing sprintf(). Lines 204 and 205 now contain the two alternative methods, sprintf() and itokhz(). Using sprintf() it's all fine. But when I use itokhz(), instead of seeing the frequency as I'd expect, the bottom line of the LCD for some reason displays the 'enc_count' variable (which is also displayed on the top line, as intended). And it updates slower than the top line (just like the frequency string should do because of the time it takes to do the averaging), so for some reason line 208 is putting 'enc_count' to the LCD instead of 'freq_char_Hz_avg'. I suspect this may be again something related to the other sprintf() on line 233, because if I comment out these four lines the display goes blank. But I would still expect the frequency to be displayed on the bottom line.I had a look at the lcd.h file and the lcd_puts() function is declared as:extern void lcd_puts(const char *s);I've not had any issues with this previously so I'm sure it's fine, but out of curiosity, if the function expects a const char, could there be a problem in passing a variable char? Seems unlikely as it would be rather limited if the char had to be const.

author: Digibin
author_rank: Regular Contributor
timestamp: January 16, 2015, 01:39:53 pm
content: Quote from: sleemanj on January 16, 2015, 01:24:40 pmOh right, that makes sense. So when people say that sprintf() needs say 2kb, that's not allocated to it at compile time? And therefore I need to ensure that there is sufficient memory left after compiling to execute it?

author: Jeroen3
author_rank: Super Contributor
timestamp: January 16, 2015, 02:01:07 pm
content: Standard library functions sometimes require C-runtime heap. Heap is the opposite of stack. You allocate something in heap using malloc. Heap is defined in the startup file. (hidden in the project settings for you avr users)Sprintf does not return anything dynamically, so it probably only uses stack.Converting an integer to a char stream is not that difficult. I used to have this routine to printout integers for debugging.Code:void sTX_putint_ascii(int data){unsigned int i;for(i=10000;i>=1;i=i/10){sTX_putchar ( ((data/i)%10)+48 ); //digit = (input / divisor) Mod 10}}Just make sure you end your string with a NULL.

author: Digibin
author_rank: Regular Contributor
timestamp: January 16, 2015, 04:15:53 pm
content: Oops, my function didn't actually operate on char_freq_Hz_avg so of course nothing is displayed. I've updated it to create the chars in a buffer array and then copy that array into 'char_freq_Hz_avg', and concatenate it with 'kHz':Code:static void itokhz(unsigned int freq, char* char_freq){char buffer;buffer = '\0';freq /= 100;// get to the hundredsbuffer=(freq % 10) + '0';// convert the hundreds to ASCII charfreq /= 10;// get to the thousandsbuffer='.';// insert decimal pointbuffer=(freq % 10) + '0';// convert the thousands to ASCII charfreq /= 10;// get to the ten thousandsif (freq)// if the ten thousands is non-zero{buffer=(freq % 10) + '0';// convert the ten thousandsfreq /= 10;// get to the hundred thousands}if (freq)// if the hundred thousands is non-zero{buffer=(freq % 10) + '0';// convert the hundred thousands to ASCII charfreq /= 10;}if (freq)// if the millions is non-zero{buffer=(freq % 10) + '0';// convert the millions to ASCII charfreq /= 10;}if (freq)// if the tens of millions is non-zero{buffer=(freq % 10) + '0';// convert the tens of millions to ASCII charfreq /= 10;}strcpy(char_freq, buffer);strcat(char_freq, " kHz");}I then call this using:char char_freq_Hz_avg;itokhz(freq_Hz_avg, char_freq_Hz_avg);Full code attached. All I get on the display is " kHz". If in the function I create a test array buffer2 and give it some arbitrary content, and then call strcpy on buffer2, it's then displayed o the LCD. So this suggests that the buffer array is empty - am I writing to it incorrectly?

author: Howardlong
author_rank: Super Contributor
timestamp: January 16, 2015, 04:43:33 pm
content: Quote from: Sal Ammoniac on January 15, 2015, 01:24:35 amSadly, next year I will have had 40 years' at it. I finally started on ARM anout six months ago having moved from PICs, with 20 years on them, and their precursor CP1600 and LP8000 chips from GI in the mid 70s, with a large helping of Z80, 6502, 6800 and x86 in between.I found with ARM I spent an awful lot of time working out the difference between the various cores, and then doing a shootout between some Cortex M4s from TI and NXP. I now use both depending on the application. But I'd say it took several weeks to get up to speed. Have you ever looked at just the clocking on the LPC4300 series? That's not trivial in itself, and that's just one small facet. You need to to learn their peripheral libraries, most of the underlying peripherals at a register and hardware level, the subtleties and features of their IDEs to name but a few. The same applies to TI, although they have the dicotomy of too much documentation, in that it's hard to know which document to start with there's so much of it.If you can port an RTOS from ARM to PIC32 in a week, mastered the IDE and debugging tools, and can present it as a finished product then well done. But don't forget, there's a lot more to a system than just that RTOS. You can spend an entire day just getting your head round DMA, or a serial peripheral for example.

author: AndreasF
author_rank: Frequent Contributor
timestamp: January 16, 2015, 05:39:05 pm
content: QuoteThere are two options for itokhz. Either eliminate the local creation of the buffer variable and use it directly with the external char array:Code://static void itokhz(unsigned int freq, char* char_freq)static void itokhz(unsigned int freq, char* buffer){//char buffer;...//char buffer2 = "0001.2";//strcpy(char_freq, buffer2);strcat(buffer, " kHz");}Or make a call to strcpy(char_freq, buffer) before strcat.I would do the fist, as there is no need to create a whole new character array inside the function, only to then strcpy it to the external char array!That's also what I was referring to earlier. You are creating a string (i.e. character array) which simply acts as a buffer of characters that then gets transmitted to the LCD. In my opinion that's wasteful, and you can directly transmit individual characters to the LCD (though you would have to start with the most significant digits).

author: Brutte
author_rank: Frequent Contributor
timestamp: January 16, 2015, 07:35:09 pm
content: Quote from: sleemanj on January 16, 2015, 01:24:40 pmStack overflows are PITA on tinies. It is a little easier on megas, but still PITA. Zillions of posts on the subject on avrfreaks - please help yourself.As for heap - avrgcc and avrlibc does not use heap implicitly. The malloc() is the only way to use heap with that library. All scanfs, printfs and alike are stack based only.@OP: My suggestion for you would be to use <assert.h>EDIT: OMG, attiny48?My suggestion for you would be to grow up.

author: Sal Ammoniac
author_rank: Super Contributor
timestamp: January 16, 2015, 08:48:44 pm
content: Quote from: Howardlong on January 16, 2015, 04:43:33 pmMy list includes 6502, Z80, 68000, x86, 8X305, AMD2901, ARM, SH, PDP-11, VAX, and several that I'm probably forgetting.QuoteYes, in fact my most recent development board for ARM has an LPC4357 on it. The clocking, although complex, is not difficult, just tedious.QuoteI never use a manufacturer's peripheral libraries. I write my own peripheral drivers directly on the bare metal. I find this takes less time than trying to understand the operation and bugs of someone else's libraries. When I write my own code, I know how it works and it's not just a black box to me.QuoteThat is true, especially for some MCUs with needlessly complex peripherals.

author: zapta
author_rank: Super Contributor
timestamp: January 16, 2015, 08:54:43 pm
content: Quote from: dannyf on January 13, 2015, 12:43:21 amNo %0n prefix for the second %lu ?

author: Digibin
author_rank: Regular Contributor
timestamp: January 16, 2015, 10:24:56 pm
content: I may have been having some issues with the LCD display. It started flashing on and off (backlight too). The supply voltage was fine so I think the LCD hardware may be going bust. Swapped it for an old one I have lying around which works fine, but is only 2x8 as opposed to 2x16.Quote from: AndreasF on January 16, 2015, 05:39:05 pmI did try it that way but couldn't get it to work. However I followed your suggestions and have modified the function to:Code:static void itokhz(unsigned long int freq, char* buffer){if (freq >= 100000){//buffer = "OVERLOAD";buffer = 'O';buffer = 'V';buffer = 'E';buffer = 'R';buffer = 'L';buffer = 'O';buffer = 'A';buffer = 'D';buffer = '\0';}else{buffer = '\0';freq /= 100;// get to the hundredsbuffer=(freq % 10) + '0';// convert the hundreds to ASCII charfreq /= 10;// get to the thousandsbuffer='.';// insert decimal pointbuffer=(freq % 10) + '0';// convert the thousands to ASCII charfreq /= 10;// get to the ten thousandsif (freq)// if the ten thousands is non-zero{buffer=(freq % 10) + '0';// convert the ten thousandsfreq /= 10;// get to the hundred thousands}else{buffer=' ';}strcat(buffer, " kHz");}}This now works, and I get the string displayed on the lcd. Due to the limit of 8 chars I can't display '100.0 kHz' so I've added an overload message in this case.The reason I couldn't get this to work before was because I was using '*buffer' instead of just 'buffer'. Doesn't 'buffer' itself store the address of the passed char variable? I thought that to modify the variable it's pointed to you need to use '*buffer'.Also, in that function there's a line commented out. If I use that line instead of setting each individual element seperately, the overload message doesn't display - instead the last frequency measurement persists on the display. Why's this?

author: Jeroen3
author_rank: Super Contributor
timestamp: January 16, 2015, 11:13:08 pm
content: Quote from: graymp89 on January 16, 2015, 10:24:56 pmUsing the initializer string literal: char *buffer = "text"; Will make buffer a pointer to the constant char array "buffer" somewhere in memory which is probably not mutable.If you want to intialize some string into buffer whilst keeping it in ram, you'd need memcpy or strcpy. Which is basically what the compiler would do implicitly if C had the syntax for it.

author: IanB
author_rank: Super Contributor
timestamp: January 16, 2015, 11:44:08 pm
content: Quote from: graymp89 on January 16, 2015, 10:24:56 pmIf you have a declaration likechar *bufferthenbufferis a pointer to a char.This being the case,*bufferresolves to a single character, not a string. The expression*bufferis identical tobuffer, since in Cbuffertranslates to*(buffer + offset).

author: Digibin
author_rank: Regular Contributor
timestamp: January 17, 2015, 01:41:47 pm
content: Quote from: Jeroen3 on January 16, 2015, 11:13:08 pmI'm not intialising it though. The string itself is already initialised before the function is called, so I just want to point to each element of the char array to edit it. Which is why I thought it should be '*buffer' because that gives the actual value stored in the address, rather than the address itself. So why does 'buffer' allow you to modify the contents of the array? In my understanding that's just giving the address of the string, not the actual values.Quote from: IanB on January 16, 2015, 11:44:08 pmOkay so how do you declare a pointer to a string? With 'char* buffer[]'?

author: Howardlong
author_rank: Super Contributor
timestamp: January 17, 2015, 01:57:53 pm
content: Quote from: graymp89 on January 17, 2015, 01:41:47 pmThe same, char *c or char *s. It's how your program (or the standard C library) interpretes it that is the difference. The language itself doesn't know the difference. What's key is that it knows the size of a char, and that has repurcussions when using the array syntax and pointer arithmetic, both of which are fundamental parts of the language.

author: AndreasF
author_rank: Frequent Contributor
timestamp: January 17, 2015, 02:11:26 pm
content: Quote from: graymp89 on January 17, 2015, 01:41:47 pmThat's an array of pointers to char variables.This might help:https://www.cs.bu.edu/teaching/c/string/intro/

author: Digibin
author_rank: Regular Contributor
timestamp: January 17, 2015, 06:04:55 pm
content: Quote from: AndreasF on January 17, 2015, 02:11:26 pmVery useful, thanks. Clears things up a lot. So C itself can't change the contents of an array, only the individual elements. And so you either change the elements individually yourself or you use the string.h library functions.Okay so having moved away from sprintf() and implemented my own conversion functions, I can now use my integer array without any issues. The original issue of the LCD displaying random characters has been resolved, so it must have been sprintf() at fault. It's strange though that the functions worked just fineuntilI tried to implement and use an integer array, even when sprintf() had nothing to do with the array.So thanks to everyone for your help. Not only is the issue resolved but my code is a lot cleaner and to be honest I understand it better now. Now I can move on to the next job - a BQ24250 battery management chip that for some reason isn't charging a flat Li-ion battery pack.