Announcement

Collapse
No announcement yet.

Baracuda + Micro

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • ivconic
    replied
    Originally posted by Teleno View Post
    ...
    A 16x2 LCD takes 39-43uS to place a character or execute a command except for clearing display and to seek cursor to home position which takes about 1.64ms. Even if you send only one character per PI cycle the refresh rate would be around 30Hz. That's 10x faster than the user would need. This is not a real time imaging application, it's just a parameter display.

    But if you implement "bar scale" to display rise/fall of amplitude at detection (or whatever else); real time is desirable. Such bar scale is perfect for pinpointing.

    Leave a comment:


  • ivconic
    replied
    Originally posted by Teleno View Post
    You're accusing others of crappy programming without a whiff of evidence and yet incapable of proposing a single line of improved code.
    You're not the only engineer here and I'm afraid there's little evidence of your pretended "superior" skills...
    So far this topic went in most positive direction! I am glad to see few very conversant and skilled experts here! Lot of to learn from you guys! Thanks in advance!
    But please calm down and don't spoil the good spirit.
    Egos aside. Personal ego is most irrelevant thing in life, trust me!
    Just keep on like you did so far!
    Best regards!

    P.S.
    I am following this with greatest attention. But i will not mix and annoy all of you because my knowledge in these matters is next to zero. Still learning.

    Leave a comment:


  • Teleno
    replied
    Originally posted by ODM View Post
    I'm just saying that a simple application shouldn't be used as an excuse to learn bad programming habits that can eventually burn ones fingers, even with a simple project like this one.

    Stubbornness is to be expected. We engineers are a stubborn breed, particularly in our home field ... when someone insists that since they are a professional of another field, they're allowed to hammer nails into the wall with their forehead. Optimization is to make things as simple as they can be, but not more simple than they should be. Like that jitter part - if it's of no consequence then it can just be ignored, but there are other very good reasons to use interrupt run timing, like being able to just write an user interface instead of having to uphill struggle in fitting it between the detector timing. And the big bonus is being able to write the detector timing separate from the user interface, so it can be independently tweaked as well.
    I think we should sit down with a beer and a couple sketchpads, and we would perhaps understand each other better!
    You're accusing others of crappy programming without a whiff of evidence and yet incapable of proposing a single line of improved code.

    You're not the only engineer here and I'm afraid there's little evidence of your pretended "superior" skills.

    Originally posted by ODM View Post
    I'd thought it nice to write an user interface that doesn't need to be done one character at a time between toggles since a LCD is part of the project. There would also be no need to stop running the detector for some cycles to process data, especially since there's just a few k of memory anyway to sample into, and any digital filters etc. are going to eat up a good few cycles on an 8bit cpu without multiply-accumulate instructions, just an 8x8 multiplier. If these are OK tradeoffs, that's all fine!
    A 16x2 LCD takes 39-43uS to place a character or execute a command except for clearing display and to seek cursor to home position which takes about 1.64ms. Even if you send only one character per PI cycle the refresh rate would be around 30Hz. That's 10x faster than the user would need. This is not a real time imaging application, it's just a parameter display.

    Leave a comment:


  • ODM
    replied
    I'd thought it nice to write an user interface that doesn't need to be done one character at a time between toggles since a LCD is part of the project. There would also be no need to stop running the detector for some cycles to process data, especially since there's just a few k of memory anyway to sample into, and any digital filters etc. are going to eat up a good few cycles on an 8bit cpu without multiply-accumulate instructions, just an 8x8 multiplier. If these are OK tradeoffs, that's all fine! I'm just saying that a simple application shouldn't be used as an excuse to learn bad programming habits that can eventually burn ones fingers, even with a simple project like this one.

    Stubbornness is to be expected. We engineers are a stubborn breed, particularly in our home field ... when someone insists that since they are a professional of another field, they're allowed to hammer nails into the wall with their forehead. Optimization is to make things as simple as they can be, but not more simple than they should be. Like that jitter part - if it's of no consequence then it can just be ignored, but there are other very good reasons to use interrupt run timing, like being able to just write an user interface instead of having to uphill struggle in fitting it between the detector timing. And the big bonus is being able to write the detector timing separate from the user interface, so it can be independently tweaked as well.
    I think we should sit down with a lot of beer, and a couple sketchpads, and we would perhaps understand each other better!

    Leave a comment:


  • Teleno
    replied
    Originally posted by ODM View Post
    Also its very nice to handle convenient large-block things such as LCD update, curve fitting, and other things besides just toggling pins on and off, in a single function thats easy to rewrite or use as it is for other projects, that doesn't have to be broken apart into sections to make for kooky, cycle-sensitive, horrible-to-maintain code is anyone's own choice. Bottomline is, I wouldn't do it for a personal project, certainly not for a hobby project meant to share with others, and if I did it in a company project I'd probably get a few good laughs in the coffee table and be fired afterwards.
    You like to make assumptions, don't you? Nowhere I recommended inlining the DSP and display code in a single finction, as you pretend. The "... " placeholder indicates where, in the timeline, such processes would take place.

    You also seem not to have read my disclamer "A PI does not need to be computation-intensive application." We're talking about an embedded controller for Baracuda here, not banking software. The idea is a controller whose total process time fits loosely inside a single PI cycle > 1ms. Your "curve fitting" argument is misplaced.

    You give the impression of being stubbornly defending a lofty point of view that's oblivious to the actual needs of the project.

    By the way, I've looked but didn't see your excelling code. I'm sure you're working on it.

    Leave a comment:


  • ODM
    replied
    If you reserve some of the AVR's 32 work registers for interrupt use only, the context shift is very short. But all in all, if most of the code it performs is known and it can update one character per cycle or something, there's no reason to run by interrupt save for safer handling of TX IO or other toggles that can burn stuff.

    That "application note" is actually someone's lecture and seems more like an example of how to start a timer and then read it, than efficient use. It's a fine and dandy way of making a delay precise to a few clocks, if the processor is all right with not doing much in the mean time. Setting up the timer, then doing useful stuff and finally polling until the timer reaches its limit, requires checking that any and all code run after the timer setup and before the polling loop fits into that time space, and doesn't stop execution like some carelessly written code can do.

    Also its very nice to handle convenient large-block things such as LCD update, curve fitting, and other things besides just toggling pins on and off, in a single function thats easy to rewrite or use as it is for other projects, that doesn't have to be broken apart into sections to make for kooky, cycle-sensitive, horrible-to-maintain code is anyone's own choice. Bottomline is, I wouldn't do it for a personal project, certainly not for a hobby project meant to share with others, and if I did it in a company project I'd probably get a few good laughs in the coffee table and be fired afterwards.

    Leave a comment:


  • Teleno
    replied
    Originally posted by ODM View Post
    But polling would take several instructions and likely response times on the order of 4-5 (sbrc, rjmp, rjmp) - it's more straightforward to use the timer interrupt if that 50-100ns jitter is of no consequence

    SBIS - Clock cycles:
    1 if condition is false (no skip)
    2 if condition is true (skip is executed) and the instruction skipped is 1 word
    3 if condition is true (skip is executed) and the instruction skipped is 2 words

    RJMP is 2 words long, this implies a latency of only 3 clock cycles in the waiting loop (188ns at 16MHz). The next instruction is already useful code.

    If you can afford it, polling it a better approach because the time to the first useful instruction is shortened. With polling, the useful code starts immediately after the condition is detected. In contrast, an ISR implies stack manipulations. These are especially costly in C because not only the PC and status registers get pushed, but a lot of other registers as well (r0 and r1 as a minimum in avr-gcc, see this example of assembly code generated: ).

    Think for example a frequency meter, polling allows measuring higher frequencies than what would be achievable by interrupts.

    See this AVR application note on page 13: http://web.csulb.edu/~hill/ee346/Lec...P%20Timers.pdf

    Leave a comment:


  • ODM
    replied
    Teleno, the kind of jitter Davor was worried about was on the degree of 1-2 clock cycles. For a 20MHz clock, each cycle would be 50ns and therefore max jitter would be 100ns. However it is "somewhat" random in nature and can be eliminated if it ever poses to be a problem, so I think this jitter thing got a bit out of hand

    But polling would take several instructions and likely response times on the order of 4-5 (sbrc, rjmp, rjmp) - it's more straightforward to use the timer interrupt if that 50-100ns jitter is of no consequence, all critical timing can go in that timer interrupt and then housekeeping, display updates, button polling etc. in the main loop. Just use the previous timer interrupt to calculate values for the next one so there will be no ambiguous code run length in the space between timer interrupt trigger and IO update. If there's switch cases etc. it'll be hard to predict actual run time.

    Leave a comment:


  • Michaelo
    replied
    Originally posted by Teleno View Post
    Another one with some assembly thrown in.
    I'd stick to your first suggestion for the loop version, in the scheme of things saving a few uS with assembly when we already have a 350uS loop doesn't give us all that much gain... I also thought about switching the pins with assembly code as I did in one of the examples but it dawned on me... just reducing one of the delays between pulse a few uS would probably gain more time...

    I guess it all depends on the remaining code but whatever happens in the remaining loop, it must execute within off period (~1300uS), otherwise we delay the pulses... I suppose it not that critical but as we can, we should...

    Mike

    Leave a comment:


  • Teleno
    replied
    Originally posted by Michaelo View Post
    That looks very interesting indeed...
    Another one with some assembly thrown in.

    PHP Code:
    #include <TimerOne.h>
    
    #define CYCLE_TIME 1662 // 0.0015625 Seconds @ 640 PPS // added 100 to 1562 to make is closer to 640pps //
    
    #define TX_PULSE         100 //(100µs)        100µs
    #define PULSE_1_DELAY     20 //( 20µs)        Delay before Sample Pulse 1
    #define PULSE_1           45 //( 45µs)        Sample 1 Pulse Duration
    #define PULSE_2_DELAY    100 //( 100µs)        Delay between Sample Pulse 1 and Pulse 2
    #define PULSE_2           45 //( 45µs)        Sample 2 Pulse Duration 45µs
    /*
     Total Pulses Time        310
     -----------------------------
     Cycle Time              1562
     Pulse Times             -310
     Delay till next cycle   1252
    
     Actual cycle time is 1/1562 µs = ~640
    */
    
    
    void setup()
    {
      cli();                              // This code needs disabled interrupts.
      pinMode (A0, OUTPUT);
      pinMode (A1, OUTPUT);  
      pinMode (A2, OUTPUT);
    
      digitalWrite (A0, LOW);
      digitalWrite (A1, LOW);
      digitalWrite (A2, LOW);  
    
      Timer1.initialize(CYCLE_TIME);  // Configures and starts Timer1
      TIMSK1 = _BV(TOIE1);            // Sets the timer overflow interrupt enable bit
    }
    
    void loop()
    {
      asm volatile(                   // Waits for TOV1 interrupt flag.
        "wait:           \n"
        "sbis %0, %1 \n"      // skip next instruction if TOV1 bit set in TIFR1
        "rjmp wait       \n"
        : : "I" (_SFR_IO_ADDR(TIFR1)), "I" (TOV1)
      );
      
      // Begin cycle
      PINC = 0x01;
      delayMicroseconds(TX_PULSE);
      PINC = 0x01;
      delayMicroseconds(PULSE_1_DELAY);
      PINC = 0x02;
      delayMicroseconds(PULSE_1) ;
      PINC = 0x02;
      delayMicroseconds(PULSE_2_DELAY);
      PINC = 0x04;
      delayMicroseconds(PULSE_2);
      PINC = 0x04;  
      // End cycle
    
     /* .... signal processing, display etc here ... */
    
      TIFR = 1 << TOV1;   // Clears TOV1 interrupt flag.
    
    } 
    

    Leave a comment:


  • Michaelo
    replied
    Originally posted by Teleno View Post
    Try this code without interrupts.
    That looks very interesting indeed...

    Leave a comment:


  • Teleno
    replied
    Try this code without interrupts.

    PHP Code:
    #include <TimerOne.h>
    
    #define CYCLE_TIME 1662 // 0.0015625 Seconds @ 640 PPS // added 100 to 1562 to make is closer to 640pps //
    
    #define TX_PULSE         100 //(100µs)        100µs
    #define PULSE_1_DELAY     20 //( 20µs)        Delay before Sample Pulse 1
    #define PULSE_1           45 //( 45µs)        Sample 1 Pulse Duration
    #define PULSE_2_DELAY    100 //( 100µs)        Delay between Sample Pulse 1 and Pulse 2
    #define PULSE_2           45 //( 45µs)        Sample 2 Pulse Duration 45µs
    /*
     Total Pulses Time        310
     -----------------------------
     Cycle Time              1562
     Pulse Times             -310
     Delay till next cycle   1252
    
     Actual cycle time is 1/1562 µs = ~640
    */
    
    
    void setup()
    {
      cli();                              // This code needs disabled interrupts.
      pinMode (A0, OUTPUT);
      pinMode (A1, OUTPUT);  
      pinMode (A2, OUTPUT);
    
      digitalWrite (A0, LOW);
      digitalWrite (A1, LOW);
      digitalWrite (A2, LOW);  
    
      Timer1.initialize(CYCLE_TIME);  // Configures and starts Timer1
      TIMSK1 = _BV(TOIE1);            // Sets the timer overflow interrupt enable bit
    }
    
    void loop()
    {
      while (TIFR1 & _BV(TOV1) == 0) {;}    // Waits for TOV1 interrupt flag.
      
      // Begin cycle
      PINC = 0x01;
      delayMicroseconds(TX_PULSE);
      PINC = 0x01;
      delayMicroseconds(PULSE_1_DELAY);
      PINC = 0x02;
      delayMicroseconds(PULSE_1) ;
      PINC = 0x02;
      delayMicroseconds(PULSE_2_DELAY);
      PINC = 0x04;
      delayMicroseconds(PULSE_2);
      PINC = 0x04;  
      // End cycle
    
     /* .... signal processing, display etc here ... */
    
      TIFR1 = _BV(TOV1);   // Clears TOV1 interrupt flag.
    } 
    

    Leave a comment:


  • Michaelo
    replied
    @ODM, Nice explanation, appreciated...

    In my tests I was seeing slight variations in an otherwise perfectly constant pulse widths (random pulses were slightly longer).
    It all depended on how I coded the delay loops, some code would produce glitches and some would not...

    Having a scope connected has proved a useful tool...

    Mike

    Leave a comment:


  • Teleno
    replied
    Originally posted by ODM View Post
    Lets say you have an overflow interrupt, as timer hits zero; first in the interrupt the ongoing instruction is processed and after that, a jump into interrupt vector (fixed code address) happens and the vector contains a jump to your interrupt routine. At the beginning of that routine, context (status register and work registers the interrupt uses) are pushed into stack, and we now see a fixed delay of cycles has passed for those pushes plus our ambiguous part (instruction in progress when interrupt triggered). We read the timer value, calculate number of no-operation instructions (NOP) we need, then relative jump into a few rows of NOP into the appropriate spot. Great, now we have jitter free code! Write outputs with previously generated values from last interrupt, generate or table-lookup new ones for next interrupt, and return.

    Or we sleep the cpu in ovf before comp interrupt a few cycles later, and we are good.
    A PI does not need to be computation-intensive application. With a 1.000Hz scanning rate you got a full millisecond to produce the Tx pulses, the sampling pulses and massage the data. ISR aren't even necessary, a simple polling loop at the end of each cycle for testing the timer interrupt flag allows you to react immediately without the uncertainty of pending instructions and the stack overhead.

    Leave a comment:


  • ODM
    replied
    The source of that jitter is instruction length in cycles. An ongoing instruction is executed before interrupt is handled. Unless running random code the interrupt isn't random

    "Otherwise perfect pulses" are perfect pulses clock by clock. Crystal osc jitter is negligible compared to skipping cycles. You need to look at the output code for your interrupt to pulse handler if you want to see the reasons. Any conditionally run code is going to add its own variable delay, so process it after updating outputs.

    Lets say you have an overflow interrupt, as timer hits zero; first in the interrupt the ongoing instruction is processed and after that, a jump into interrupt vector (fixed code address) happens and the vector contains a jump to your interrupt routine. At the beginning of that routine, context (status register and work registers the interrupt uses) are pushed into stack, and we now see a fixed delay of cycles has passed for those pushes plus our ambiguous part (instruction in progress when interrupt triggered). We read the timer value, calculate number of no-operation instructions (NOP) we need, then relative jump into a few rows of NOP into the appropriate spot. Great, now we have jitter free code! Write outputs with previously generated values from last interrupt, generate or table-lookup new ones for next interrupt, and return.

    Or we sleep the cpu in ovf before comp interrupt a few cycles later, and we are good.

    But there is nothing magic about it, this is all in the complete datasheets. Nothing is going to spoil "perfect pulses" if the processor isn't told to do so

    Leave a comment:

Working...
X