How to Use a PIC16F873A for IR proximity, school project,simple but help needed

Status
Not open for further replies.

MoRoH

New Member
Hi all,

I am an electronics engineering technology student doing my final project.

I need to use a PIC16F873A to take an analog voltage and turn motor A on if it is less then 4 and motor B on if it more than a 4.

This is a very simple task, I have some ideas but i have no experience with PIC's.


Thanks in advance
 
Ok,

I will use a reference voltage of 5v and if V>2.5 motor A will come on and if v < 2.5 V motor B will come on? Is this correct

I am also wondering what type of code is best to use?

What ports should i use for input and output?



Thanks, any help it greatly appriciated
 
Virtually any port that has the peripheral you need. If you want to read an analog voltage, use one of the analog pins. If you want to output a signal, use virtually any pins. If you want to output a PWM signal to vary the speed of the motor, use an OC pin.

The best code to use is the kind you know.
 
hmm ADC code

ok,

1) Can someone clarify an OC pin, is that an oscillator pin

2)So i've looked into this a bit more...I'm going to use an analog input (RA0) and output a 8bit binary word on ports (RB0-RB7) for LED. I will also like to output a PWM signal so the stronger the analog signal is the slower the motor will spin.

3) For the ADC it seems pretty simple...from examples i've gotten this code, but it comes with errors. Help would be awesome.

line/column message no. message text
a) 1.3 11 ';' expected by ADCON1 found
b) 1.5 4 specifier needed
c) 1.5 12 internal error




Code:
unsigned short adc_val  ;
void main ( )

ADCON1 = 0x00  ;
TRISA  = 0xff  ;
TRISB  = 0x00  ;
while (1)
(
adc_val = adc_read(2)    ;
portb = adc_val     ;
)

)
 
OC is Output Compare, the name doesn't seem all that obvious to me unless you know how the OC pin works. In general, an output compare works by comparing a period timer to a counter value.

First, you set up a timer to run at some frequency, a good rule of thumb for a motor is 20kHz.
Set your period register to some value
The hardware will compare the timer value to the period register and set/clear the output pin based on if the timer is greater than, equal to, or less than the period register.
When you want to change the duty cycle, you just change the period register.

I've only used OC on PIC24 and PIC32 so I can't help with absolute specifics on PIC16, but it should work the same way. It may be called something other than OC on PIC16.

As for the errors in your code, you may need to post more of the code. The only thing I see wrong with what you have is that you're missing a '{' after void main(). Also, ADCON1 I think should be set to 0xFE as you only need RA0 to be analog. TRISA should be set to 0x01 to make RA0 an input and the rest an output.

With a digital input that is floating, if the pin is say 2.5v, then both the PFET and the NFET on the input gate will be partially on, which will cause additional current to flow through the chip. It won't hurt anything, but in general, it is good practice to set all unused pins to output high/low or input with pull-ups or pull-downs.

Of course, read your datasheet and make sure I'm not telling you wrong about this

Hmm.... I don't use names like "unsigned short" so I don't know how many bits wide it is (I know I should as that is fairly basic...) but I think it is 8 bit. The ADC's in PICs are generally 10 bits, which means if your function adc_read() returns a 16 bit value, you are probably losing the 2 most significant bits. What you should do is change the ADC setup to left adjust the ADC result and return only the high byte of the value (if you don't need 10 bits of resolution). This could also be done by simply shifting the result to the right by 2, but it is more efficient to have the ADC module left adjust the result and read only one byte. The datasheet will have more information on this and explain it in detail.
 
Last edited:

Hi there,

Where did you get that code? And is that all the code? Assuming it's supposed to be C, and assuming that that's all the code there is, there is a lot wrong with it.

As Noggin pointed out, it doesn't have an opening curly brace for the main function. Actually, it doesn't have a closing brace either; the closing "brace" is actually a right parenthesis (meaning it's a ")" when it should be a "}").

Same with the while() loop. It should look like

Code:
// Right.
while (1) 
{
    // loop contents go here
}

. . .instead of:

Code:
// Wrong.
while (1) 
(
    // loop contents go here
)

The code uses ADCON1, TRISA, TRISB, adc_read(), and portb without any of these variables and functions having been defined. There should be an #include directive to include the header for the chip you're using.

What compiler are you using?

(BTW: No offense intended if you wrote that code; everybody has to start somewhere. I'm just trying to point out reasons it might not be working.)


Regards,

Torben

[Edit: Hey, you're from Corner Brook! I had a very good night followed by a very bad morning in Corner Brook a few years back. Newfs have to be the friendliest folk I've ever met.]
 
Last edited:
HEy


First of all glad you had a good night here, not so glad you had a bad morning but the better the night the worse the morning.


I'm new to PIC's, and im not an engneering student, my schooling is mostly focused on troubleshooting, and the hardware stuff. I have very little design and programming altough ive used the 8255 and assembly a fair bit and a small course on C.


To get more knowledge ive been doing tutorials on simple programs like blinking LED and this ADC i found on youtube, but i couldint get it working. but now that i got this one working i will adapt it to my PIC and move on.


The { vs ( was my main problem! thanks for the hints guys. I will update you on my progress after some more research.


Thanks for the replys, I really appriciate it!
 
ok,

so i got my code adapted to my 16F873A now inputs A0 (A1-A4 are off) B0-b7 are set to outputs.. so I get my output on the simulator using this code:

Nogin, you were right about ADCON1 and TRISA thanks

Code:
unsigned short adc_val  ;
void main ( )
{

ADCON1 = 0xFE  ;
TRISA  = 0x01  ;
TRISB  = 0x00  ;
while (1)
{
adc_val = adc_read(0)    ;
portb = adc_val     ;
}

}



Next is how to get PWM output.
 
Question : i'm using MikroC for my programming...the #include code includes libraries from which i will use code right? how do i add libraries?

Answer : unknown
 
Hi there!

Just a quick note: Make sure you setup the acquisition time correctly to assure correct readings... This is explained very well in the datasheet.
 
ok, thanks, ive been reaidng the data sheets and commands for PWM, when i get some code made up, ill post it with the errors (99.999% sure there will be errors)

Thanks

PS: i got the library thing sorted out, just a misunderstanding
 
Getting there!..i think

ok, this is the code i have made..errors encounterd as expected.

My GOAL : take an anlog voltage in to port RA0, put an 8 bit word on port B and get PWM varying proportinal to the analog input, on port C!

Some help would be awesome

Code:
unsigned short in;
unsigned short inmem;
unsigned short adc_val;

void InitMain() {
  PORTB = 0;                 // Set PORTB to 0
  TRISB = 0x00;              // PORTB is output

  ADCON1 = 0xfe;             // All ADC pin 1 to on
  TRISA = 0x01;            // PORTA is input

  PORTC = 0;            // Set PORTC to
  TRISC = 0x00;              // PORTC is output
  Pwm_Init(5000);         // Initialize PWM module
}

void main() {
  InitMain();
  in  = adc_val;              // Initial value on analog input
  inmem  = 0;                // remembers nalog input
  Pwm_Start();               // Start PWM

  while (1) {                // Endless loop
    adc_val = adc_read(0)    // read analog value on RA0
      if adc_val > inmem       //if analog value goes higer then last time
      in++                    //make duty cycle greater
      if adc_val < inmem        //if analog value is lower than last time
      in--                       // make duty cycle smaller

    if (inmem != in) {        // If change in duty cycle requested,
      Pwm_Change_Duty(in);    //   set new duty ratio,
      inmem = in;             //   memorize it,
      PORTC = inmem;         //   and display on PORTC
    }
    Delay_ms(200);        // Slow down a bit
  }

while (1)
{
adc_val = adc_read(0)    ;   //read analog input Ra0
portb = adc_val     ;         //output 8bt on port B
}

}


}
 
Don't know what this is (I could guess what it is trying to do...): Pwm_Init(5000); // Initialize PWM module
in = adc_val; Doesn't do anything worthwhile because adc_val hasn't been initialized
Pwm_Start(); Again, don't know what this function does
adc_val = adc_read(0) needs a ; at the end, and don't know what adc_read(0) does
if adc_val > inmem should be if (adc_val > inmem)
in++ needs a ; (I think this would be better named "duty_cycle" but its your code)
if adc_val < inmem should be if (adc_val < inmem)
in-- should have a ;

You also have an extra } at the end

Code:
while (1) {                // Endless loop
    adc_val = adc_read(0)    // read analog value on RA0
      if adc_val > inmem       //if analog value goes higer then last time
      in++                    //make duty cycle greater
      if adc_val < inmem        //if analog value is lower than last time
      in--                       // make duty cycle smaller

    if (inmem != in) {        // If change in duty cycle requested,
      Pwm_Change_Duty(in);    //   set new duty ratio,
      inmem = in;             //   memorize it,
      PORTC = inmem;         //   and display on PORTC
    }
    Delay_ms(200);        // Slow down a bit
  }

might be simplifiable down to
Code:
while (1)
{
	adc_val = adc_read(0);
	if (adc_val > duty_cycle)
		Pwm_Change_Duty(++duty_cycle);
	else if (adc_val < duty_cycle)
		Pwm_Change_Duty(--duty_cycle);

	PORTC = duty_cycle;
	Delay_ms(200);
}
 
PWMINIT = Initializes the PWM module with duty ratio 0. Parameter freq is a desired PWM frequency in Hz (refer to device data sheet for correct values in respect with Fosc).
Pwm_Init needs to be called before using other functions from PWM Library.
Initialize PWM module at 5KHz:

Pwm_Init(5000);


PWM Start = Description ,Starts PWM.
Example Pwm_Start();

ADC_read(0) = Read analog voltage on port RA0

Thats what i got from the hepl files in MicroC. Thanks noggin i'll keep working on it, appriciate your help. i've made some changes you suggested.
 
I have some code that has no errors. But its still don't think its working...I have a couple of questions.

1) what port/ports does my PWM come out of in RC0-RC7 and how do i smiulate it

2) Im trying to say If my analog voltage is more then before speed up the motor...if its less then before slow it down..is this code correct (Thanks to Noggin).. Im wondering if it know what duty_cycle is? do i have to set duty_cycle to something?

Code:
while (1)
{
	adc_val = adc_read(0);             //read analog input RA0
	if (adc_val > duty_cycle)               //   if value is higher
		Pwm_Change_Duty(++duty_cycle);   //then increase duty
	else if (adc_val < duty_cycle)            //if value is lower
		Pwm_Change_Duty(--duty_cycle);    //lower duty

	PORTC = duty_cycle;                   //output PWM on C
	Delay_ms(200);                       //slow down process
}
 
I'm not familiar with MicroC, those functions are provided by it? If so, then I guess we can assume that they work correctly. I tend to not use functions provided by the compiler but would rather roll my own (easier to do with experience).

Unless you set duty_cycle to some value, it'll start out at 0. The Pwm_Change_Duty(++duty_cycle) will increase duty_cycle by 1 and then call the function Pwm_Change_Duty and pass the new duty_cycle value to it.

does adc_read() return an 8 bit value or a 16 bit value? If it returns a 16 bit value, then you're losing the two most significant bits.
Pwm_Change_Duty( duty_cycle ) what is the allowable range for duty_cycle? Is Pwm_Change_Duty( 50 ) yield a 50% duty cycle? 5 %? 50 out of 5000 (1%)?
Since adc_val is an 8 bit value, it can only go to 255. If adc_val is 255, do you want that to be a duty_cycle of 100%?

The way you have it written, the duty_cycle will come out on every pin of PORTC. If duty_cycle is 97, PORTC will be 0b01100001 (assuming no peripherals have control of a pin on PORTC)

The code as written will continually speed up the motor until the duty_cycle is equal to adc_val. I think your original code will too as well. Your original code isn't triggering off of a change in analog voltage, its triggering off of a difference between analog voltage and "inmem" which is the same as your duty cycle.

I don't know for certain, but it looks like CCP1 is the output for PWM mode. You dont have to do anything with PORTC (other than set CCP1 to an output) for the PWM to work. Once the hardware is set up, and you load the duty_cycle and period into the proper registers, the hardware will control the pin for you.
 
Last edited:
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…