Saturday, February 4, 2012

Just having a little fun

This circuit was meant to test an idea but the idea turned out to be a dead end. So I ended up just playing with the circuit. Red, green and blue LEDs are turned on in sequence. Because of the difference in light intensity I've used different resistors values for each of the colors to try and even the output. Position of the potentiometer is read by the PIC16F1824's ADC. The two least significant bits of the 10-bit ADC output are ignored and the 8-bit value is copied to TMR0 and timer0 sets the amount of time each LED is on.

The white cylinder over the LEDs is just an old milky-white colored 35mm film canister. But it makes a heck of difference on how the light output is seen by the human eye. Certainly far more interesting with it. By varying the direction in which the LEDs point different light patterns on the cylinder can be created.

The MCU is run at 1MHz. LED switching occurs in the interrupt service routine. Maximum switching frequency is approximately 325Hz--each LED is on for just around 1ms. Minimum frequency is around 1.27Hz. Period T = on-time of each LED x number of LEDs. Frequency = 1/T. Timer0 is used for timing the LEDs. Its prescale is set to 256. With the clock running at 1MHz, time for one instruction cycle = 4/1MHz = 4µs. Timer0 tick = TMR0 x 256 x 4µs. The reset value of TMR0 can range from zero to 255 depending on the position of the potentiometer.

Timer2 is used to periodically read the pot and store its value in a variable which then gets copied to TMR0 during every timer0 interrupt.




#define  int1                bit
#define  int8                unsigned char
#define  int16               unsigned int
#define  int32               unsigned long

#define  on                  1
#define  off                 0

#define  _on                 0
#define  _off                1

#define  yes                 1
#define  no                  0

#define  input               1         // for TRISx
#define  output              0         // for TRISx

#define  analog              1         // for ANSELx
#define  digital             0         // for ANSELx

#define  lred                LATC.f0             // red led
#define  lgreen              LATC.f1             // green led
#define  lblue               LATC.f2             // blue led

#define  tris_lred           TRISC.f0            // for setting LED pin as output
#define  tris_lgreen         TRISC.f1            // for setting LED pin as output
#define  tris_lblue          TRISC.f2            // for setting LED pin as output

#define  an_lred             ANSELC.f0           // for setting LED pin as digital
#define  an_lgreen           ANSELC.f1           // for setting LED pin as digital
#define  an_lblue            ANSELC.f2           // for setting LED pin as digital

#define  ch_pot              3                   // analog channel of potentiometer

// ==================================================================================================================
//          global variables
// ==================================================================================================================

int8 TMR0COUNT;

// ==================================================================================================================
//          functions
// ==================================================================================================================

void IniReg()
{
  OSCCON    = 0b1011000;     // internal clock = 1MHz

  // set LED pins as output
  tris_lred = 0;
  tris_lgreen = 0;
  tris_lblue = 0;
  
  // set LED pins as digital
  an_lred = 0;
  an_lgreen = 0;
  an_lblue = 0;

  lred = 1;                  // start off with red LED
  
  // Timer0 setup
  OPTION_REG = 0b10000111;   // Weak pull ups disabled
                             // timer0 uses internal clock,
                             // prescaler assigned to timer0, prescaler = 1:256
  TMR0 = 0;
  INTCON.TMR0IE = 1;         // timer0 interrupt enable
  
  // Timer2 setup
  // with a 1MHz clock, prescale = 1:16, postscale = 1:1, PR2 = 200
  // timer2 tick = 250 * 64 / (1MHz/4) = 64ms
  T2CON = 0b111;             // timer2 on, prescale = 1:64, postscale = 1:1
  PR2 = 250;                 // timer2 counts up from zero until the value of PR2 and then resets

  // Analog to Digital Coverter setup
  ADCON0 = ch_pot << 2;      // shift in analog channel to be made active
  ADCON0.ADON = 1;           // turn on ADC
  ADCON1 = 0;                // left justified, Fosc/2, Vss as negative reference, Vdd as positive reference

  INTCON.GIE = 1;            // global interrupt enabled
} // void IniReg()


/* =========================================================================================================

According to PIC12F1824 Silicon Errata sheet DS80510D the ADC unit in certain silicon revisions is buggy:

"An ADC conversion may not complete under these conditions:
1. When FOSC is greater than 8 MHz and it is the clock source used for the ADC converter.
2. The ADC is operating from its dedicated internal FRC oscillator and 
the device is not in Sleep mode (any FOSC frequency). 
When this occurs, the ADC Interrupt Flag (ADIF) does not get set, the GO/DONE bit does not get cleared, 
and the conversion result does not get loaded into the ADRESH and ADRESL result registers."

mikroC Pro compiler's Adc_Read() built-in ADC function uses the FRC oscillators o it cannot be used.

The workaround used here is as per method 1 in the said errata:
"Select the system clock, FOSC, as the ADC clock source 
and reduce the FOSC frequency to 8 MHz or less when performing ADC conversions."

=========================================================================================================  */

int8 ADC()
{
  ADCON0.GO = 1;                       // start ADC conversion
  while (ADCON0.GO) ;                  // just wait until AD conversion is done
  return ADRESH;                       // return only the 8 most significant bits of the 10-bit value
}


void interrupt()
{
  if (INTCON.TMR0IF)
  {
    TMR0 = TMR0COUNT;
    if (lred)
    {
      lgreen = on;
      lred = off;
    }
    else if (lgreen)
    {
      lblue = on;
      lgreen = off;
    }
    else
    {
      lred = on;
      lblue = off;
    }
    INTCON.TMR0IF = 0;
  }
}


void main()
{
  IniReg();

  while(1)
  {
    if (PIR1.TMR2IF)
    {
      TMR0COUNT = ADC();
      PIR1.TMR2IF = 0;
    }
  } // while(1)
} // void main()

No comments:

Post a Comment