-
bash aliases WHOOPS
02/27/2017 at 16:48 • 0 commentsI can't believe I've never run into this before...
I've had this in my .bash_profile for *years*
alias mmv='echo "" ; echo "DON'\''T GET COCKY! mv -i set in .bash_profile, this is NOT default. " ; echo " Default is to overwrite WITHOUT PROMPTING" ; echo "" ; /bin/mv -i'
Now I did:
diff file1 file2 && mmv file1 otherDir
-----------
Now, apparently, 'alias' doesn't act like a script or a function, as I'd long-understood.
Apparently it literally just pastes the contents on the line... so what I'm getting, instead is essentially:
diff file1 file2 && echo "" ; ..... ; mv file1 otherDir
I have a *really* hard time believing I never ran into this before. REALLY hard time. Literally *years* I've used this alias... and never *once* ran into this?! But, that's the claim.
So, those ';'s need to be replaced with '&&'.
-----------
And upon looking at the alias with fresh eyes, I should've seen that it wasn't treated like a function... since it's not using arguments...
-
Interrupts, Volatiles, and Atomics... and maybe threads?
11/13/2016 at 16:54 • 0 commentsWhen using global-variables shared between your main() function and an interrupt (or two threads?), there are a lot of potential disasters one might not expect...
----------
I'm no expert, here... but I've learnt a few things along the way, which could be handy for others. There's probably an entire course in most Computer-Science degrees on the matter, but maybe this is a decent starting-point for those who haven't run into it...
----------
First: Use "volatile" correctly...
Here's a pretty great writeup on the matter, so I won't go into it: http://www.barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword
Read that, because some of the following relies on your having-done...
---------
Now for the fun stuff... That page doesn't describe several other common pitfalls...
E.G. say you have:
while( volatile_int != 0 ) { do something with volatile_int }
Will it reread volatile_int *each* time it's referenced within that while-loop, or will that entire iteration of the while loop use that value of volatile_int? Or worse, maybe:
if( volatile_int > 0 ) { do something with volatile_int } else if ( volatile_int < 0 ) { do something else with volatile_int } else // volatile_int == 0 { do something else, again, with volatile_int }
In cases like these, it's too confusing (for me) to rely on whatever the standard might claim in some vague reference-manual somewhere hard to find... Nevermind what would happen when an interrupt occurs between testing the first "if" and the next "else if"...
So best to use a separate *local* variable... e.g.
int temp_int; while( (temp_int = volatile_int) !=0 ) { do something with temp_int }
or
int temp_int = volatile_int; if( temp_int > 0 ) { do something with temp_int } else if ( temp_int < 0 ) { do something else with temp_int } else // temp_int == 0 { do something else, again, with temp_int }
And, when needing to write back to volatile_int...?
Best to write to a temp variable and *only once* write that temp-variable back to the global.
Even still, there's some difficulty. If your interrupt *and* main both write to the same variable you can get into some trouble, depending on the implementation... This is where mutual-exclusion comes into play, and I thankfully haven't had to mess with that much.
---------
(For most of my needs, an interrupt writes a variable, main reads it back, and *if* main needs to write it, it's usually just as an indicator that the value has been processed... more on that in a second).
---------
And, finally, it may not be *as* common on a 32-bit device, but definitely a common problem with 8-bitters... Consider a global volatile int64_t being written in an interrupt and used in main():
temp_int64 = volatile_int64;
On a 32-bit system, that will most-likely require *two* instructions, each writing 32-bits of temp_int, one 32-bit word at a time. If the interrupt occurs *between* those writes, temp_int64 will contain a mess. Here's a 16-bit example on an 8-bit processor:
volatile_int16 = 0x00ff; main: temp_int16 = volatile_int16; // -> temp_int16[low byte] = 0xff; // <INTERRUPT> interrupt: volatile_int16 = 0xff00; // <return to main> main: temp_int16 = volatile_int16; //(continued) // -> temp_int16[high byte] = 0xff; //now, in main, temp_int16 == 0xffff
Bad news!
This one's a real problem, because most people don't see it just looking at the code. *And* it happens *very* rarely that the interrupt will occur at *exactly* that moment.
But if you're not careful the consequences could be horrendous
(sudden-acceleration of cars? pace-makers firing when not needed? etc. etc. etc.)
So, then, it's somewhat common to change it in main:
<disable interrupts>; temp_int16 = volatile_int16; <reenable interrupts>
This makes the two-instruction assignment "atomic", it cannot be interrupted.
Of course, this assumes you already had interrupts enabled and want them reenabled after your assignment to temp_int16... but what if you don't *know* whether interrupts are enabled but want to make sure your assignment to temp_int16 remains atomic? It gets a bit more complicated... so don't just go throwing around cli() (clear interrupts, on AVRs) and sei() (set interrupts) willy-nilly.
There are probably more-common and better ways of handling this...
E.G. on AVRs there's "ATOMIC_BLOCK()"
Check out http://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html for some more examples.
----------
Personally, ATOMIC_BLOCK is a bit too complex for my normal needs, ("WTF is this argument?!") so I've written "CLI_SAFE" and "SEI_RESTORE" for most architectures I've worked with... here's the AVR example:
#define CLI_SAFE(uint8_name) (uint8_name) = (SREG); cli() #define SEI_RESTORE(uint8_name) (SREG) = (uint8_name)
Now I'd rewrite the temp_int16 assignment (in main) as:uint8_t oldInterruptState; CLI_SAFE(oldInterruptState); temp_int16 = volatile_int16; SEI_RESTORE(oldInterruptState);
One handy side-effect of how it's written... you don't need the explicit declaration of oldInterruptState:
uint8_t CLI_SAFE(oldInterruptState); temp_int16 = volatile_int16; SEI_RESTORE(oldInterruptState);
----------
So, earlier I said that for most of my needs, an interrupt writes a variable, main reads it back, and *if* main needs to write it, it's usually just as an indicator that the value has been processed, so it won't be processed again.
E.G.
volatile int16_t receivedByte = -1; void interrupt_handler(void) { //Unfortunately, this'll overwrite the last value // if not processed quickly-enough by main()! receivedByte = <read UART Rx-Register>; } int main(void) { while(1) { uint8_t CLI_SAFE(oldState); int16_t tempRx = receivedByte; receivedByte = -1; SEI_RESTORE(oldState) if( tempRx >= 0 ) { //display the hex-value of the byte received: printf( "Received 0x%2x", tempRx); } } }
Note how the read *and* modification of receivedByte occur within the same "atomic block" in main.Anything more complicated than that is too much for this write-up... Just be aware of these pitfalls, and don't assume that I actually know what I'm talking about, and *don't* use my code/explanations for designing self-driving cars or anything else that could harm a person, fergodsakes.
-
Oh My... TRUE = NON-ZERO pitfalls
07/03/2016 at 20:24 • 6 commentsHah. somehow it'd never occurred to me... Hopefully because I never needed it like this... otherwise there may be bugs all over the place completely unknown.
---------------
So, "TRUE" in the sense of
if(something){<do something else>}
... is whenever something is non-zero. NON-ZERO.Right, that's easy... and quite handy.
Checking whether a pin is high on a port...?
if( PORTA_INPUT_REGISTER & (1<<pinNum) ) { ... }
does the trick quite nicely...And, with optimization, that's nothing more than a <read-register> and an <and> (the left-shift is turned into a constant, assuming pinNum is a constant).
Okie Dokie!
So now, let's use that somewhere a bit more complex... right? Not sure *why* exactly, but say you want to for some reason...
e.g.
uint8_t portInputs = PORT_INPUT_REGISTER; if ( portInputs & (1<<pinNum) ) { ... }
Awesome.Now say you're using that as an output to a function... why-not-eh?
//Returns NON-ZERO when the pin is high //ZERO when it's low uint8_t IsPinHigh(uint8_t pinNum) { return ( PORT_INPUT_REGISTER & (1<<pinNum) ); } ... if(IsPinHigh(3)) { ... }
Woot, it's all good...
So, doing-so this way saves quite a few instructions... Another way of doing something like this would be to have IsPinHigh() return ONE (the value, 0x01) if the pin is high, rather than NON-ZERO. But, doing-so requires an additional test, a non-zero test. which I guess would probably only be one or two instructions, but they're instructions, nonetheless. Another way, still, (and more obvious to me, for some reason) would be to >> the result back, e.g.:
uint8_t IsPinHigh(uint8_t pinNum) { return ( ( PORT_INPUT_REGISTER & (1<<pinNum) ) >> pinNum ); }
So, now you've got a ton of instructions... but your output is either 0 or 1, rather'n 0 or (1<<pinNum).Anyways, we *know* it's going to be used *only* in cases where non-zero or zero is what matters... e.g. in if(IsPinHigh()) statements, so why waste all those extra instructions?
Now, who knows, maybe you want it to be a little more complex, still... I dunno why... (back to the Zero v. Non-Zero example):
Or, heck, don't even bother making it more complex... We've got a pretty good setup, here. It works great!
Awesome. Now move that same bit of code to another processor, maybe a 32-bitter, where the Port-Registers are 16bits wide...
//Returns NON-ZERO when the pin is high //ZERO when it's low uint8_t IsPinHigh(uint8_t pinNum) { return ( PORT_INPUT_REGISTER & (1<<pinNum) ); } ... if(IsPinHigh(13)) { ... }
Whoops.
Duh.
See it?
I mean, all we care about is a boolean value, yahknow, it's either true or not true... That *easily* fits in a uint8_t. But... Yeah. Probably not worth all this explanation, really, because yahknow it's so blatantly obvious, here... Even more fun when you're porting code that's already been written and long-functional and it's not all spelled-out for you like this.
And worse, still, when the return-value of IsPinHigh() is stored in *yet another* variable, which may well be uint8_t as well...
Oy.
So, here's a nastier example where it might be *slightly* less-obvious.
//Returns NON-ZERO when the pin is high //ZERO when it's low uint8_t IsPinHigh(uint8_t pinNum) { return ( PORT_INPUT_REGISTER & (1<<pinNum) ); } //If isTrue is NON-ZERO, then doSomething //If isTrue is ZERO, then don't doSomething void doSomethingWhenTrue(uint8_t isTrue) { if(isTrue) { ... } } ... int pinIsHigh = IsPinHigh(3); doSomethingWhenTrue(pinIsHigh);
Or, imagine you saw the glaring fact of IsPinHigh()'s return-value, and fixed it *right away*, but instead of pinIsHigh having been an int, instead it was a uint8_t... Or, yahknow, there might be any number of combinations wherein it might even get missed by -Wconversion (see below).FYI: -Wconversion is a handy argument to throw at gcc... but you'll also get to see just how many conversions happen behind the scenes that you don't usually think about... E.G. did you know that ~(0xff) becomes an *int* (rather than the uint8_t it started as)? So, typedef or #define a new type for a case like this. Run it once as the uint8_t it was, capture the output from gcc (with all those gazillions of new warnings) and run it again with the new uint16_t (or 32) and *diff* the outputs from the two runs... helpful, but probably not guaranteed to find *every* case.