Shop OBEX P1 Docs P2 Docs Learn Events
How is this possible? (adc read problem) — Parallax Forums

How is this possible? (adc read problem)

turbosupraturbosupra Posts: 1,088
edited 2014-10-19 17:45 in Propeller 1
I'm basing a 2.3hz pwm signal off of an ADC read. The signal is inconsistent randomly and will flatline at times for a few seconds. In troubleshooting this I discovered that a lot of times when I read an ADC value, the value is being returned as zero, when I know that I am feeding the mcp3208 pin a constant analog voltage for testing. Is there some sort of read write lock issue going on here? As you can see, within the same loop it will read 2 different values and the only thing I can think of is that I'm being locked out from a read operation, during a write operation? In the top screen capture I set the value for adcTHW to a hard coded 1000 (bypassing the ADC read function entirely) and the behavior still continued? In the bottom I let it read from adc, but tried to have it consistently update a temp variable, and then let me assign it to the comparison variable right before entering the case statement. If this is a problem with a lock, does anyone have a work around for this coding wise? Flags? I do not need a high speed read, a 2hz or 1hz update cycle would be fine.

Thanks

pub main

 repeat
   
     adcTHWTemp := adc.read(4, adc#SE)




pub pwmOut

  adcTHW := adcTHWTemp


      if (adcTHW <> 0)
      
        [b]debug.str(string("adcTHW is "))
        debug.dec(adcTHW)      
        debug.tx(13)[/b]
         
        case -1   
         
          (adcTHW => 3482) : ' 20C or 68F = 4.25vDc or 3482 ADC value (this is a guess at vDc and ADC)
            onTime := f.fmul(c_20CelsiusOn, pwmOutputHertzToClkCycles)
            onTime := f.fround(onTime)
            offTime := f.fmul(c_20CelsiusOff, pwmOutputHertzToClkCycles)
            offTime := f.fround(offTime)
            'onTime := 32863070 ' 99% was 35200000
            'offTime := 331950 ' 1% was 355556
            if (coolantPwmDebug == true)  AND ((cnt - coolantPwmDebugPause) > 0)
              coolantPwmDebugPause := (coolantPwmDebugPauseTime + cnt)
              coolantPwmDebugLoopOverride := true
              debug.dec(adcTHW)
              debug.Tx(13)          
         
            if (coolantPwmStateHigh == false) AND ((cnt - coolantPwmOffTime) > 0)
              coolantPwmStateHigh := true   
              outa[O_TempGauge] := 1            
              coolantPwmOnTime := (onTime + cnt)
            
            if (coolantPwmDebug == true) AND (((cnt - coolantPwmDebugPause) > 0) OR (coolantPwmDebugLoopOverride == true)) 
              debug.str(string("20C On"))      
              debug.tx(13)      
            
            if (coolantPwmStateHigh == true) AND ((cnt - coolantPwmOnTime) > 0)
              coolantPwmStateHigh := false
              outa[O_TempGauge] := 0
              coolantPwmOffTime := (offTime + cnt)
              
            if (coolantPwmDebug == true) AND (((cnt - coolantPwmDebugPause) > 0) OR (coolantPwmDebugLoopOverride == true))
              coolantPwmDebugLoopOverride := false
              debug.str(string("20C Off"))
              debug.tx(13)       
            
...
[many other case statements]
...


        (adcTHW < 82) AND (adcTHW => 0) : ' AND (adcTHW => 82) :
           [b] debug.str(string("<<<<< 82"))
            debug.tx(13)
            debug.dec(adcTHW)
            debug.tx(13)
            debug.dec(adcTHW)
            debug.tx(13) [/b]   



adc_Read_Fail1.jpg

adc_Read_Fail2.jpg

Comments

  • turbosupraturbosupra Posts: 1,088
    edited 2014-10-17 05:24
    I changed my code to use adc.read(4, adc#SE) instead of the variable that should have held that value.

    The zeros are less common, but still occur.

      if (adcTHW <> 0)
          
            debug.str(string("adcTHW is "))
            [b]debug.dec(adc.read(4, adc#SE))      [/b]
            debug.tx(13)
    
    
      (adc.read(4, adc#SE) < 82) AND (adc.read(4, adc#SE) => 0) : ' AND (adcTHW => 82) :
                debug.str(string("<<<<< 82"))
                debug.tx(13)
                [b]debug.dec(adc.read(4, adc#SE))[/b]
                debug.tx(13)
                [b]debug.dec(adc.read(4, adc#SE))[/b]
                debug.tx(13) 
    

    adc_Read_Fail3.jpg
  • pmrobertpmrobert Posts: 673
    edited 2014-10-17 05:46
    VAR
      long  OKtoRead
    
    pub main
    
     repeat
    
         repeat until OKtoRead = 1
         OKtoRead := 0;
         adcTHWTemp := adc.read(4, adc#SE)
         OKtoRead := 1;
         pause 50
    
    
    pub pwmOut
    
      repeat until OKtoRead = 1
    
      OKtoRead:=0
      adcTHW := adcTHWTemp
      OKtoRead:=1
    
    

    Something like the above pseudocode has worked for me previously when I experienced a similar problem.

    You could also use locks I suppose...

    Discalimer: The syntax of the code fragment above is way, way wrong - I don't do much Spin - but the logic is hopefully apparent.
  • turbosupraturbosupra Posts: 1,088
    edited 2014-10-17 05:53
    Hi,

    Thanks for the reply. So if I understand correctly, you've had a similar adc read issue and you used flags to allow the cogs to avoid a read issue?
  • JonnyMacJonnyMac Posts: 9,105
    edited 2014-10-17 08:54
    Do you have a spare cog? If yes, you could put the ADC read function it such that it can provide values to your program as an array of longs; instead of calling the ADC, your program would just pick up the latest value(s) from a group. I've done this many times (in fact, I thought I shared this with you already). This bit of code will read all channels every 10ms, and provide a rolling average of the last four readings. I used this in a camera controller project and it worked quite well.
    var
    
      long  adccog
      long  adcstack[32]
      
      long  pot[8]                                                  ' averaged pots
    
      long  accidx
      long  acc[32]                                                 ' 4 readings for 8 pots
      
    
    pri start_adc
    
      if (adccog)                                                   ' stop if already running
        cogstop(adccog-1)
    
      adccog := cognew(scan_pots, @adcstack) + 1 
      
    
    pri scan_pots | t, ofs, ch, sum
    
    '' Scans and averages adc inputs
    '' -- averages adc inputs over four readings
    
      adc.start(ADC_CS, ADC_CLK, ADC_DIO)                           ' mcp3208 (Spin)  
    
      t := cnt
      repeat
        ofs := accidx << 3                                          ' offset into accumulator array
        repeat ch from 0 to 7                                       
          acc[ch + ofs] := adc.read(ch, adc#SE)                     ' read channels
          
          sum := acc[ch]                                            ' rolling average
          sum += acc[ch + 08]
          sum += acc[ch + 16]
          sum += acc[ch + 24]  
          pot[ch] := sum >> 2
          
        if (++accidx == 4)
          accidx := 0
    
        waitcnt(t += (10 * MS_001))
    
  • turbosupraturbosupra Posts: 1,088
    edited 2014-10-17 10:20
    Hi Jon,

    I don't have a spare cog, but I can have a spare cog with some movement ... my one spare cog is being used to simulate something for testing but I can disable it and replace it with that function if that is the best way.

    Right now it was running inside of the main cog, I have tried the flag suggestion and it is working on the bench right now, I do not know if it will work out in the "field" yet. I had considered doing averaging but had not gotten that far yet, so thank you for that example as well. I will test tonight/tomorrow and report back. :)

    JonnyMac wrote: »
    Do you have a spare cog? If yes, you could put the ADC read function it such that it can provide values to your program as an array of longs; instead of calling the ADC, your program would just pick up the latest value(s) from a group. I've done this many times (in fact, I thought I shared this with you already). This bit of code will read all channels every 10ms, and provide a rolling average of the last four readings. I used this in a camera controller project and it worked quite well.
    var
    
      long  adccog
      long  adcstack[32]
      
      long  pot[8]                                                  ' averaged pots
    
      long  accidx
      long  acc[32]                                                 ' 4 readings for 8 pots
      
    
    pri start_adc
    
      if (adccog)                                                   ' stop if already running
        cogstop(adccog-1)
    
      adccog := cognew(scan_pots, @adcstack) + 1 
      
    
    pri scan_pots | t, ofs, ch, sum
    
    '' Scans and averages adc inputs
    '' -- averages adc inputs over four readings
    
      adc.start(ADC_CS, ADC_CLK, ADC_DIO)                           ' mcp3208 (Spin)  
    
      t := cnt
      repeat
        ofs := accidx << 3                                          ' offset into accumulator array
        repeat ch from 0 to 7                                       
          acc[ch + ofs] := adc.read(ch, adc#SE)                     ' read channels
          
          sum := acc[ch]                                            ' rolling average
          sum += acc[ch + 08]
          sum += acc[ch + 16]
          sum += acc[ch + 24]  
          pot[ch] := sum >> 2
          
        if (++accidx == 4)
          accidx := 0
    
        waitcnt(t += (10 * MS_001))
    
  • JonnyMacJonnyMac Posts: 9,105
    edited 2014-10-17 10:49
    It's difficult to provide really good help without having your code. The fragments you provide -- for me, anyway -- don't provide enough clues into what's happening. The idea that you're using flags suggests to me that you have multiple cogs wanting to access the ADC. This can be a problem because the ADC code is Spin (if the CS line is made high by any cog, others cannot pull it low).
  • turbosupraturbosupra Posts: 1,088
    edited 2014-10-17 17:33
    I understand that seeing the code would be easier, the reason I didn't post my whole code is that it is a lot of code, I only have about 600 free longs and have about 4000 lines of code and didn't want it to be so overwhelming. If you are open to looking at it, that would be much appreciated.

    I only had one cog (pub main) reading the adc object and then set a global variable value to the read adc value. I was getting a 0 value when reading the global variable from the second cog and was trying to troubleshoot why thinking that it may have been an issue that when the second cog was trying to read the global variable, the first cog (pub main) was writing to it and there was a lock issue.

    All that being said, I wrote the code for the flags and I also found a typo in my code. When I came home and tested the code, it worked. I'm not sure if the flags or the corrected typo fixed it. I am also going to implement your suggestion of a rolling average, as I think that is a great idea. If I run into problems again, I will attach all of my code and objects if the size is not discouraging ... if the size is, I'll move the failing parts to a new smaller project, verify the behavior is still there and attach that.

    JonnyMac wrote: »
    It's difficult to provide really good help without having your code. The fragments you provide -- for me, anyway -- don't provide enough clues into what's happening. The idea that you're using flags suggests to me that you have multiple cogs wanting to access the ADC. This can be a problem because the ADC code is Spin (if the CS line is made high by any cog, others cannot pull it low).
  • JonnyMacJonnyMac Posts: 9,105
    edited 2014-10-17 20:27
    Uh, no. If you have 4000+ lines of code and are having problems with a basic function in the program, you may have an architecture issue. That's not something anyone can help you with. I've written a couple commercial programs that are that size (and larger) -- it's very important that the basic structure of the program is solid before filling it out. Not what you wanted to read, I'm sure.
  • pmrobertpmrobert Posts: 673
    edited 2014-10-18 06:54
    turbosupra wrote: »
    Hi,

    Thanks for the reply. So if I understand correctly, you've had a similar adc read issue and you used flags to allow the cogs to avoid a read issue?

    Yes, that is correct. I also use Jon's approach as well especially if I have multichannel ADC acquiring multiple channels. A separate cog reading (and optionally averaging) the ADC values then writing the processed values to a global hub variable works nicely. You can then read the global variable by any other cog without fear of getting an anomalous value. I almost certainly learned these techniques from Jon and others, a tip of the hat to all who have unknowingly helped me!

    -Mike
  • turbosupraturbosupra Posts: 1,088
    edited 2014-10-19 17:45
    Between the bug and the flag, the code seems to be functioning well. I still want to implement averaging, but I want to test it as is first for a while. Either way, I appreciate the offer :) . Thanks to both of you.
    JonnyMac wrote: »
    Uh, no. If you have 4000+ lines of code and are having problems with a basic function in the program, you may have an architecture issue. That's not something anyone can help you with. I've written a couple commercial programs that are that size (and larger) -- it's very important that the basic structure of the program is solid before filling it out. Not what you wanted to read, I'm sure.
    pmrobert wrote: »
    Yes, that is correct. I also use Jon's approach as well especially if I have multichannel ADC acquiring multiple channels. A separate cog reading (and optionally averaging) the ADC values then writing the processed values to a global hub variable works nicely. You can then read the global variable by any other cog without fear of getting an anomalous value. I almost certainly learned these techniques from Jon and others, a tip of the hat to all who have unknowingly helped me!

    -Mike
Sign In or Register to comment.