Guest User

PioneerController-ProtoThread.ino

a guest
Jun 2nd, 2022
193
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Arduino 18.95 KB | None | 0 0
  1. #include "X9C.h"
  2. #include <Time.h>
  3. #include "pt.h"
  4.  
  5. // PINS
  6.  
  7. // Pins for Rotatary Encoder
  8. #define             clkPin                         2     // CLK PIN on encoder in 2
  9. #define             dtPin                          3     // DT Pin on encoder in 3
  10. #define             swPin                          4     // SWPin on encoder in 4
  11.  
  12. // Pins for digital Pot
  13. #define             CS                             13
  14. #define             UD                             12
  15. #define             INC                            11
  16.  
  17. // Globals
  18. #define             NumOfStepsNeeded               1    // given the travel, you might want increase the number of steps needed for a volume move (my encoder seems too aggressively moving)
  19.  
  20.  
  21.  
  22. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  23. // for debugging do a find/replace on Serial with (slash)(slash)Serial
  24. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  25.  
  26. // Encoder vars
  27. int                 EncoderValue                  = 0;
  28. int                 PreviousEncoderVal            = 0;
  29. int                 Delta                         = 0;
  30. int                 PreviousPush                  = 0;
  31. static int          oldA                          = HIGH;
  32. static int          oldB                          = HIGH;
  33.  
  34. // time vars
  35. unsigned long       CurrentMills                  = 0;
  36. unsigned long       PreviousMills                 = 0;
  37. static int          DoubleClickTime               = 350;         // how much time between single click and if another "double" click
  38. static int          DeBounceDelay                 = 10;          // this is hardware/software loop centric, for my setup and loop() 10 seems to be working well
  39. static int          MinSliceDelay                 = 1;           // 20200831 - maybe the min slice should be the WaitForUnitComplete time?
  40. static int          MoreAggressiveDebounce        = 200;
  41.  
  42.  
  43. // Threading times                                              //  libraries scheduler seem to need different CPUs, and protothread seemed to involved, so just did a simple wait schedule with anti-stravation
  44. static int          WaitForUnitToComplete         = 101;        // 41 so far it looks like the Pioneer might need 40msec to respond to the event
  45. static int          WaitForDisplayTime            = 550;        // 20200728 was 850 but sometimes still misses some it seems   // was 650  // this should be the minimum time to display the screen
  46. static int          WaitTimeForBetweenScreens     = 4200;       // this should be the minimum time for the screen to remove after no other commands have been sent
  47.  
  48. // hold down button for Mute method
  49. static bool         HoldDownForMute               = true;      // this is just a toggle to see if you are using this method of muting
  50. static int          HowLongToHoldForMute          = 500;       // this probably should be more than the doubleclicktime (cause less confusion)
  51.  
  52. // ProtoThreads
  53. static struct pt pt1, pt2;                                      // 2 threads, the encodes pt1 thread, and the writing of commands (the POT setter) pt2
  54.  
  55. // ProtoThread Queue
  56. #define               QUEUEMAXSIZE  200
  57. int                   QueueCommands[QUEUEMAXSIZE];
  58. int                   QueueIndex                    = 0;
  59. int                   QueueLastProc                 = 0;
  60. static int            LoopOfThread                  = 0;
  61. static int            CurLimit                      = 0;
  62.  
  63. // 20200714 -- you must now place the defines for anything that causes the Pioneer VolumeScreen to come up first and contiguous so that SCREENRANGE is < all of them
  64. #define VOLUMEUP      1
  65. #define VOLUMEDOWN    2
  66. #define SCREENRANGE   3         // MUTE actually does NOT bring up the screen, so screenrange should be lower
  67.  
  68. #define MUTE          3         // mute can be implemented as the button MUTE or PLAY/PAUSE which would allow Navigation to continue, I usually opt for that method
  69. #define TRACKFF       4
  70. #define TRACKPV       5
  71. #define TRIPLECLICK   6       // unsure what function this would do, mostly testing right now
  72.  
  73.  
  74. // create a global POT object
  75. X9C pot;
  76.  
  77. // in the case of the X9C pot, its a percentage and the 104 is 100K with 100 steps so each step is 1000 Ohm or 1K so you just set the percentage directly
  78. //
  79.  
  80. #define REST_VOLUMEUP                              15
  81. #define REST_VOLUMEDOWN                            23
  82. #define REST_TRACKFF                               7
  83. #define REST_MUTE                                  2
  84. #define REST_TRACKPV                               10
  85. #define REST_TRIPLECLICK                           0
  86.  
  87.  
  88.  
  89. void setup()
  90. {
  91.   // setup encoder
  92.   pinMode(clkPin,INPUT);
  93.   pinMode(dtPin,INPUT);
  94.   pinMode(swPin,INPUT);
  95.   digitalWrite(swPin,HIGH);
  96.  
  97.   // setup POT
  98.   pot.begin(CS, INC, UD);
  99.   delay(1);
  100.   pot.setPotMax(true);
  101.   delay(WaitForUnitToComplete);
  102.  
  103.   // clear the queue, I believe this can be removed since the units memory starts zero'ed, (I remember reading that somewhere)
  104.   ClearQueue();
  105.  
  106.   // Setup output
  107.   Serial.begin(115200);
  108.   Serial.println("Started - forcing MaxWaits ... ");
  109.   delay(WaitTimeForBetweenScreens+100);                                    // 20200901 - the arduino must run WaitForScreen for the WaitDisplay to work, (normally the bootup to CarPlay is much longer, but this is here to make sure the testing block works
  110.   Serial.println("Started!!");  delay(WaitTimeForBetweenScreens+100);      // 20200901 - the arduino must run WaitForScreen for the WaitDisplay to work, (normally the bootup to CarPlay is much longer, but this is here to make sure the testing block works
  111. }
  112.  
  113. void loop()
  114. {
  115.   protothread1(&pt1);
  116.   protothread2(&pt2);
  117. }
  118.  
  119. static int protothread1(struct pt *pt)
  120. {
  121.   static unsigned long timestamp            = 0;
  122.   static int           change               = 0;
  123.  
  124.   static unsigned long LastTimeDirection    = 0;  
  125.   static int           LastDirection        = 0;
  126.  
  127.   static unsigned long PrevLongHoldTime     = 0;
  128.   static unsigned long StillHoldingDown     = 0;
  129.  
  130.   PT_BEGIN(pt);
  131.  
  132.   //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  133.   // 20200831 - testing without an encoded ((or to test a scenario over and over) -- forcing encoder commands direct into stream
  134.   if (0)
  135.   {  
  136.       Serial.println("IF PRODUCTION, remember to set this IF to IF (0) !!!!!!!!!!!!!!");        
  137.       LastDirection=VOLUMEUP;
  138.       PulseVolumeUp();
  139.       timestamp = millis(); PT_WAIT_UNTIL(pt, millis() - timestamp > MinSliceDelay);                        // allow other thread some time            
  140.       PulseVolumeUp();
  141.       timestamp = millis(); PT_WAIT_UNTIL(pt, millis() - timestamp > MinSliceDelay);                        // allow other thread some time            
  142.       PulseVolumeUp();
  143.       timestamp = millis(); PT_WAIT_UNTIL(pt, millis() - timestamp > MinSliceDelay);                        // allow other thread some time            
  144.   }
  145.   // 20200831 - testing without an encoded ((or to test a scenario over and over) -- forcing encoder commands direct into stream
  146.   //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  147.  
  148.   while(1)
  149.   {
  150.       // get encode info
  151.       change              = getEncoderTurn();      
  152.       PreviousEncoderVal  = EncoderValue;
  153.       EncoderValue        = EncoderValue+change;
  154.    
  155.       // see if they clicked, and if they did, and the time has expired, then track forward
  156.       if (PreviousMills != 0 && millis()-PreviousMills>DoubleClickTime)
  157.       {
  158.           // you have to see if you are holding down just in case because you dont want to track AND mute
  159.           if (HoldDownForMute && PrevLongHoldTime)
  160.           {
  161.             Serial.println("You are still holding down, dont track forward until you let go");        
  162.           }
  163.           else
  164.          {
  165.             PreviousMills =0;
  166.             PulseTrackForward();        
  167.          }
  168.       }
  169.        
  170.    
  171.       // reset if you push the button, that will probably change to track forward, and 2 is track back
  172.       if(digitalRead(swPin) == LOW)
  173.       {
  174.         // this is the handler for Muting now
  175.         if (HoldDownForMute)
  176.        {
  177.           if (PrevLongHoldTime && millis()-PrevLongHoldTime > HowLongToHoldForMute && !StillHoldingDown)          
  178.           {
  179.               StillHoldingDown = millis();
  180.               Serial.println("You have held down long enough");            
  181.               ClearQueue();              PulseMute();
  182.               //timestamp = millis(); PT_WAIT_UNTIL(pt, millis() - timestamp > WaitForUnitToComplete);                        // allow other thread some time            
  183.               Serial.println("DONE FORCE MUTE");
  184.               PreviousMills=0; // this is here to make sure that the next command isnt a TrackForward
  185.           }
  186.           if (PrevLongHoldTime == 0)
  187.             PrevLongHoldTime = millis();
  188.        }
  189.        
  190.         if (PreviousPush==0)
  191.         {
  192.            CurrentMills = millis();            
  193.            if (PreviousMills==0)
  194.            {
  195.              PreviousMills = CurrentMills;
  196.            }
  197.            else if (CurrentMills-PreviousMills<DoubleClickTime)      
  198.            {
  199.                 PreviousMills =0;
  200.                 PulseTrackBack();
  201.            }
  202.            PreviousPush = 1;
  203.         }        
  204.         timestamp = millis(); PT_WAIT_UNTIL(pt, millis() - timestamp > DeBounceDelay);
  205.       }
  206.       else
  207.       {
  208.           PreviousPush = 0;
  209.           if (HoldDownForMute)
  210.           {  
  211.               PrevLongHoldTime = 0;          
  212.               StillHoldingDown = 0;
  213.           }
  214.       }
  215.      
  216.      
  217.       if (EncoderValue !=  PreviousEncoderVal)
  218.       {        
  219.          Delta = Delta + EncoderValue - PreviousEncoderVal;            
  220.          if (Delta == NumOfStepsNeeded || Delta == -NumOfStepsNeeded)
  221.          {          
  222.            if (Delta >0)
  223.            {                
  224.                 Delta=0;                                
  225.                 if (LastDirection==VOLUMEDOWN && millis() - LastTimeDirection < MoreAggressiveDebounce)
  226.                 {
  227.                   Serial.println((String)"This UP is being ignored, TimeDiff~=" + (millis() - LastTimeDirection));                  
  228.                 }
  229.                 else
  230.                 {
  231.                   PulseVolumeUp();                
  232.                   LastDirection=VOLUMEUP;
  233.                 }
  234.                 LastTimeDirection=millis();
  235.            }
  236.            else
  237.            {
  238.                 Delta=0;  
  239.  
  240.                 if (LastDirection==VOLUMEUP && millis() - LastTimeDirection < MoreAggressiveDebounce)
  241.                 {
  242.                   Serial.println((String)"This DOWN is being ignored, timediff~= " + (millis() - LastTimeDirection));
  243.                 }
  244.                 else                
  245.                 {
  246.                   PulseVolumeDown();
  247.                   LastDirection=VOLUMEDOWN;                  
  248.                   LastTimeDirection=millis();
  249.                 }
  250.              }
  251.          }
  252.       }
  253.       timestamp = millis(); PT_WAIT_UNTIL(pt, millis() - timestamp > MinSliceDelay);                        // allow other thread some time            
  254.   }
  255.   PT_END(pt);
  256. }
  257.  
  258. static int protothread2(struct pt *pt)
  259. {
  260.   static unsigned long  timestamp                      = 0;  
  261.   static uint16_t       Command                        = 0;
  262.   static unsigned long  PreviousCommandTimeStamp       = 0;  
  263.   static bool           WaitForDisplay                 =false;  
  264.   static unsigned long  TimeWhenInQueue                = 0;  
  265.   static bool           InsideAQueueProcess            =false;  
  266.   static int            TempCommand                    =0;
  267.  
  268.  
  269.   PT_BEGIN(pt);
  270.   while(1)
  271.   {    
  272.      Command=0;    
  273.       CurLimit=QueueIndex;
  274.       if (CurLimit<QueueLastProc)
  275.         CurLimit=CurLimit+QUEUEMAXSIZE;
  276.  
  277.       if (QueueLastProc != CurLimit)
  278.       {
  279.         Serial.println((String)"QueueIndex="+QueueIndex+" QueueLastProc="+QueueLastProc+ " CurLimit="+CurLimit+" LoopOfThread="+LoopOfThread);        
  280.         for (LoopOfThread = QueueLastProc; LoopOfThread < CurLimit; LoopOfThread++)
  281.         {                  
  282.             Command=0;
  283.             switch(QueueCommands[LoopOfThread%QUEUEMAXSIZE])
  284.             {              
  285.               case VOLUMEUP:    
  286.                 Command = REST_VOLUMEUP;
  287.                 Serial.println("UP ");
  288.                 break;
  289.               case VOLUMEDOWN:
  290.                 Command = REST_VOLUMEDOWN;              
  291.                 Serial.println("DOWN ");
  292.                 break;
  293.               case TRACKFF:
  294.                 Command = REST_TRACKFF;              
  295.                 Serial.println("FF ");
  296.                 break;
  297.               case TRACKPV:
  298.                 Command = REST_TRACKPV;                            
  299.                 Serial.println("PV ");
  300.                 break;
  301.               case MUTE:
  302.                 Command = REST_MUTE;                       
  303.                 Serial.println("MUTE ");
  304.                 break;
  305. //              case TRIPLECLICK:
  306. //                Command = REST_TRIPLECLICK;            
  307. //                Serial.print("TRIPLECLICK ");
  308. //                break;                
  309.               default:
  310.                 Serial.println((String)"Dont think I should hit these QueueIndex="+QueueIndex+" QueueLastProc="+QueueLastProc+ " CurLimit="+CurLimit);
  311.                 Command=0;
  312.                 timestamp = millis();PT_WAIT_UNTIL(pt, millis() - timestamp > DeBounceDelay);    
  313.               break;                
  314.             }
  315.             TempCommand = QueueCommands[LoopOfThread%QUEUEMAXSIZE];
  316.             QueueCommands[LoopOfThread%QUEUEMAXSIZE]=0;      
  317.            
  318.             if (Command != 0)
  319.             {  
  320.                 timestamp = millis();
  321.  
  322.                 Serial.println((String)"timestamp="+timestamp+" PreviousCommandTimeStamp="+PreviousCommandTimeStamp+ " InsideAQueueProcess="+InsideAQueueProcess);
  323.                
  324.                 if (timestamp-PreviousCommandTimeStamp > WaitTimeForBetweenScreens && !InsideAQueueProcess && TempCommand < SCREENRANGE)    // SCREENRANGE must be +1 then ALL display impacting cases
  325.                 {
  326.                   Serial.println("Flag to WaitForDisplay");
  327.                   PreviousCommandTimeStamp = timestamp;
  328.                   WaitForDisplay=true;
  329.                 }
  330.  
  331.                 InsideAQueueProcess=true;
  332.                 TimeWhenInQueue = millis();                
  333.                
  334.                 pot.setPot(Command,false);
  335.                 timestamp = millis();PT_WAIT_UNTIL(pt, millis() - timestamp > WaitForUnitToComplete);                       // allow stereo time to handle the input, and allow more inputs to come in
  336.                 Serial.println("CommandDone");
  337.  
  338.                 if (WaitForDisplay)
  339.                 {
  340.                    Serial.println("Waiting for Screen");                
  341.                    WaitForDisplay = false;
  342.                    
  343.                    // 20200728 - make sure to turn the POT oFF because a volume command still caused this issue
  344.                    Serial.println("Waiting for Screen-but still SetPotMax");  
  345.                    pot.setPotMax(true);                           // you dont have to wait here since we are already waiting for the screen
  346.  
  347.                    timestamp = millis();PT_WAIT_UNTIL(pt, millis() - timestamp > WaitForDisplayTime);                // allow stereo time to handle the input
  348.                    Serial.println("Screen should be up, do any other commands");
  349.                 }
  350.                 else
  351.                 {
  352.                   // 20200831 - this had to be removed from below because if you are in the first loop of 3 volume up, you are not outside of the loop to wait, (not sure that really matters though)
  353.                   Serial.println("SetPotMax");
  354.                   pot.setPotMax(true);
  355.                   timestamp = millis();PT_WAIT_UNTIL(pt, millis() - timestamp > WaitForUnitToComplete);                       // allow stereo time to handle the input                  
  356.                 }
  357.             }
  358.             Command=0;
  359.         } // when the for-next loop is done (all the commands on the queue)      
  360.         QueueLastProc=CurLimit%QUEUEMAXSIZE;
  361.  
  362.         // 20200831 - I think you still need to set the resistance HIGH once all the commands are done
  363.         // 20200728 - you only need to set PotMAX after all the commands are done -- (just a little more efficient)
  364.         pot.setPotMax(true);
  365.         timestamp = millis();PT_WAIT_UNTIL(pt, millis() - timestamp > WaitForUnitToComplete);                       // allow stereo time to handle the input
  366.  
  367.       }
  368.       else // if you are here there was no messages in the queue
  369.       {
  370.         timestamp = millis();
  371.         if (timestamp-TimeWhenInQueue > WaitTimeForBetweenScreens)
  372.         {
  373.            if (InsideAQueueProcess)
  374.               Serial.println("The screen is no longer on ");  
  375.            InsideAQueueProcess=false;
  376.         }
  377.       }
  378.       timestamp = millis(); PT_WAIT_UNTIL(pt, millis() - timestamp > MinSliceDelay);                                // allow other thread some time
  379.   }
  380.   PT_END(pt);
  381. }
  382.  
  383. //  commands
  384. void PulseVolumeUp()
  385. {
  386.   QueueCommands[QueueIndex]=VOLUMEUP;
  387.   IncreaseQueueIndex();
  388.   Serial.println("PULSE-UP");
  389. }
  390. void PulseVolumeDown()
  391. {
  392.   QueueCommands[QueueIndex]=VOLUMEDOWN;
  393.   IncreaseQueueIndex();
  394.   Serial.println("PULSE-DOWN");
  395. }
  396. void PulseTrackForward(void)
  397. {
  398.   QueueCommands[QueueIndex]=TRACKFF;
  399.   IncreaseQueueIndex();
  400.   Serial.println("PULSE-FF");  
  401. }
  402. void PulseTrackBack(void)
  403. {
  404.   QueueCommands[QueueIndex]=TRACKPV;
  405.   IncreaseQueueIndex();
  406.   Serial.println("PULSE-PV");    
  407. }
  408. void PulseMute(void)
  409. {
  410.   QueueCommands[QueueIndex]=MUTE;
  411.   IncreaseQueueIndex();
  412.   Serial.println("PULSE-MUTE");    
  413. }
  414. void PulseTripleClick(void)
  415. {
  416.   QueueCommands[QueueIndex]=TRIPLECLICK;
  417.   IncreaseQueueIndex();
  418.   Serial.println("PULSE-TRIPLE");    
  419. }
  420.  
  421. int getEncoderTurn(void)
  422. {
  423.   int result=0;
  424.   int newA=digitalRead(clkPin);
  425.   int newB=digitalRead(dtPin);
  426.   if (newA != oldA || newB != oldB)
  427.   {
  428.     // something changed
  429.     if (oldA == HIGH && newA==LOW)
  430.       result = (oldB*2-1);
  431.   }
  432.   oldA=newA;
  433.   oldB=newB;
  434.   return result*-1;
  435. }
  436.  
  437.  
  438. void IncreaseQueueIndex()
  439. {  
  440.     QueueIndex++;
  441.     if (QueueIndex > QUEUEMAXSIZE)
  442.       QueueIndex=0;
  443. }
  444.  
  445. void ClearQueue()
  446. {
  447.   //init the queue, (not really needed but just in case)
  448.   static int clearloop=0;
  449.   CurLimit=0;
  450.   LoopOfThread=0;
  451.   QueueIndex=0;
  452.   QueueLastProc=0;      
  453.   for (clearloop=0;clearloop<=QUEUEMAXSIZE;clearloop++)
  454.      QueueCommands[clearloop]=0;
  455.    
  456. }
  457.  
  458.  
Advertisement
Add Comment
Please, Sign In to add comment