Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[PIC] 16F887 > 2 axis joystick drive 2 servomotors

Status
Not open for further replies.

Eric_O

Advanced Member level 4
Joined
May 31, 2020
Messages
104
Helped
0
Reputation
0
Reaction score
0
Trophy points
16
Activity points
996
My code here under works :

- Axis X drives servo LOWER (Timer2 and Timer0), portd.bo
- Axis Y drives servo UPPER (Timer2 and Timer1)

But ...

- servo LOWER turns too fast in both directions, left and right while a very short shifting on left or right way with joystick
- servo UPER turns too slowly step after step in both directions, left and right while a very short shifting on up and down way with joystick

Question :

How could I control turns slowly while shifting joystick on the all scale of the displacement of the stick ?

Part of the code :

Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
while(1)
{
  // Servomotor LOWER 270° : TOWER PRO M996R (270°)
  Init_Timer2();
  Init_Timer0();
  p0 = &TMR0;
  joystick_X = (ADC_read(0));             // Reads Y axis value with channel 0 (AN0) on pin #2.
  //if ((joystick_X <= 500) && (joystick_X > 0))
if ((joystick_X <= 500) && (joystick_X > 100))
{
//consigne_0 = 256 - 226; // consigne_0 = 256 - 226 => 226 à 256 = 30 x 1 uS = 30 uS = 0,03 mS.
// ROTATION CONTINUE DROITE.
//consigne_0 = 256 - (0.25 * joystick_X);
consigne_0 = joystick_X / 4;
      //consigne_0 = joystick_X / 5;
      sprintl(*p0, "%3u", consigne_0);
      Init_Timer2();
      Init_Timer0();
      while(cnt0 < (consigne_0 / 10))
{
portd.b1 = 0;
portd.b0 = 1; // Servomotor LOWER ON.
portc.b2 = 1; // LED ON.
      }
      portd.b0 = 0;
portc.b2 = 0;
     }
  //if ((joystick_X >= 524) && (joystick_X < 1024))
if ((joystick_X >= 524) && (joystick_X < 924))
{
//consigne_0 = 256 - 156; // consigne_0 = 256 - 156 => 156 à 256 = 100 x 1 uS = 100 uS = 0,1 mS.
// ROTATION CONTINUE GAUCHE.
//consigne_0 = 256 - (0.25 * joystick_X);
consigne_0 = joystick_X / 4;
      //consigne_0 = joystick_X / 5;
      sprintl(*p0, "%3u", consigne_0);
      Init_Timer2();
      Init_Timer0();
      while(cnt0 < (consigne_0 / 10))
{
portd.b1 = 0;
portd.b0 = 1; // Servomotor LOWER ON.
portc.b2 = 1; // LED ON.
      }
      portd.b0 = 0;
portc.b2 = 0;
     }
  if ((joystick_X > 500) && (joystick_X < 524))
{
portd.b1 = 0;
portd.b0 = 0;
portc.b2 = 0;
     }
  // Servomotor UPPER 360° : TOWER PRO M996R (360°) (Ancien 270° avec les 2 butées détruites)
  Init_Timer2();
  Init_Timer1();
  p1 = &TMR1L;
  joystick_Y = (ADC_read(1));             // Reads Y axis value with channel 1 (AN1) on pin #3.
  //if ((joystick_Y <= 500) && (joystick_Y > 250))
if ((joystick_Y <= 490) && (joystick_Y > 250))
{
portd.b0 = 0;
consigne_1 = 65535 - 1500; // consigne_1 = 65535 - 1500 => 64035 à 65535 = 1500 x 1 uS = 1,5 mS = Ton.
// ROTATION CONTINUE GAUCHE LENTE.
sprintl(*p1, "%5u", consigne_1); // In Library Manager check box Sprintl and check box C_Type are selected automatically.
Delay_ms(1000); // Délais AJUSTABLE pour définir l'angle de rotation.
     }
  if ((joystick_Y <= 250) && (joystick_Y > 0))
{
portd.b0 = 0;
consigne_1 = 65535 - 1750; // consigne_1 = 65535 - 1750 => 63785 à 65535 = 1750 x 1 uS = 1,75 mS = Ton.
// ROTATION CONTINUE GAUCHE RAPIDE.
sprintl(*p1, "%5u", consigne_1); // In Library Manager check box Sprintl and check box C_Type are selected automatically.
Delay_ms(1000); // Délais AJUSTABLE pour définir l'angle de rotation.
     }
  //if ((joystick_Y >= 524) && (joystick_Y < 774))
if ((joystick_Y >= 534) && (joystick_Y < 774))
{
portd.b0 = 0;
consigne_1 = 65535 - 1400; // consigne_1 = 65535 - 1400 => 64135 à 65535 = 1400 x 1 uS = 1,4 mS = Ton.
// ROTATION CONTINUE DROITE LENTE.
sprintl(*p1, "%5u", consigne_1); // In Library Manager check box Sprintl and check box C_Type are selected automatically.
Delay_ms(1000); // Délais AJUSTABLE pour définir l'angle de rotation.
     }
  if ((joystick_Y >= 774) && (joystick_Y < 1024))
{
portd.b0 = 0;
consigne_1 = 65535 - 1250; // consigne_1 = 65535 - 1250 => 64285 à 65535 = 1250 x 1 uS = 1,25 mS = Ton.
// ROTATION CONTINUE DROITE RAPIDE.
sprintl(*p1, "%5u", consigne_1); // In Library Manager check box Sprintl and check box C_Type are selected automatically.
Delay_ms(1000); // Délais AJUSTABLE pour définir l'angle de rotation.
     }
  //if ((joystick_Y > 500) && (joystick_Y < 524))
if ((joystick_Y > 490) && (joystick_Y < 534))
//if ((joystick_Y > 250) && (joystick_Y < 774))
{
portd.b0 = 0;
portd.b1 = 0;
portc.b2 = 0;
}
}


Thanks for your help !
Merci beaucoup.

Eric
--- Updated ---
 
Last edited by a moderator:

hello ,

post all the code
included Timerx init and interrupts treatment

but i think this will not work fine at all..
if treatment stay in main program for both joystick


ADC reading could be inside Main loop
but Output for both Servo must be inside TMR0 interrupt
else to much time disturbance if treatment stay in main loop

Also take care for the scaling between ADC value and time duration
to respect
One way => 1ms to 1,5mS
and for the Other way => 1,5mS to 2 mS
1,5mS +-0.02 no move

i suppose elementary delay TMR0 is 10µS

some idea

Code:
 joystick_X = (ADC_read(0));      
if ((joystick_X <= 500) && (joystick_X > 5))
{
// ROTATION CONTINUE DROITE.
.....
   // consigne_0 = joystick_X / 4;
	consigne_0 = 100 + (joystick_X/10) ;   //  so 100 + (0 to 50) => x 10uS =>  1mS à 1,5mS
.....


if ((joystick_X >= 512) && (joystick_X < 1012))
{
// ROTATION CONTINUE GAUCHE.
//consigne_0 = joystick_X / 4;
 consigne_0 = 150+(joystick_X-512) / 10;  // so  150 +(0 to 50)=>*10µS => 1.5 à 2mS
......
 if ((joystick_X > 500) && (joystick_X < 512))
  {
 portd.b1 = 0;
 portd.b0 = 0;
 portc.b2 = 0;
   }
 

    Eric_O

    Points: 2
    Helpful Answer Positive Rating
Bonjour Paul,
Thanks for your advises. I will try them as soon as possible.
In the attached file you will find the full code.
Sorry for my long comments. They help me for my understanding, the first time, and when working again on after long pauses.
 

Attachments

  • 2 axes joysticks drives 2 servos.txt
    14.9 KB · Views: 101

hello,


you don't need to built the command signal into the main , if allready done inside interrupts ..
at least only ADC reading are inside the main loop.
we have to discuss about that ..

instead to post a *.txt file
could you post a *.zip
wich contains all the project :
*.mccpi
*.cfg
*.log
*.c
*ihex ( if EEPROM used )

so, to be able to rebuild the *.hex with same conditions
for testing purpose ..
 

hello,


you don't need to built the command signal into the main , if allready done inside interrupts ..
at least only ADC reading are inside the main loop.
we have to discuss about that ..

instead to post a *.txt file
could you post a *.zip
wich contains all the project :
*.mccpi
*.cfg
*.log
*.c
*ihex ( if EEPROM used )

so, to be able to rebuild the *.hex with same conditions
for testing purpose ..
Bonjour Paul,
Even with your explanations I have no idea how to do yet. I will need your help again. While waiting for your answer I will try on my side ...
Here attached the zip file.
Merci beaucoup ! 👍
 

Attachments

  • Two_servomotors_with_three_timers_with_joystick.zip
    92.5 KB · Views: 96

Bonjour Paul,
Even with your explanations I have no idea how to do yet. I will need your help again. While waiting for your answer I will try on my side ...
Here attached the zip file.
Merci beaucoup ! 👍


Ok, i will have a look ... entre 2 etapes du TDF ...
 

Your code is hard to read and hard for me to understand, let alone try to fix.

For my own amusement, I wrote code from scratch. MikroC project attached.

I make no claims to the quality or reliability of the code, but it appears to work as long as I use separate power supplies for servos compared to PIC board (but common ground)

Fell free to use or ignore as you please....
 

Attachments

  • forum.zip
    26.7 KB · Views: 89

Hello Everybody,

Tour De France

TDF Restart today 16em Etape
:( unfortunatly, no French in TOP 10 general ranking !
 

Hello Everybody,

Tour De France

TDF Restart today 16em Etape
:( unfortunatly, no French in TOP 10 general ranking !
🚴🏼 Oooohhhh ! I See ! Too bad ! 😕😔
 

Your code is hard to read and hard for me to understand, let alone try to fix.

For my own amusement, I wrote code from scratch. MikroC project attached.

I make no claims to the quality or reliability of the code, but it appears to work as long as I use separate power supplies for servos compared to PIC board (but common ground)

Fell free to use or ignore as you please....
Thanks for your help HexReader.
Your code looks more simple ... and interresting ... I did study it. Compilation OK with MikroC too. I will run your code tonight with 2 leds. I don’t have my 2 servo yet with me.
Will let you know.
Merci.
Eric
PS :
Yes, I already use a 6 VDC power supply only for both servo with common GND with 5 VDC PIC programmer.
 

hello,

i saw a problem in
unsigned int ADC_read(unsigned char channel)
the channel can NEVER CHANGE
because not affeted to ADCON0 register
and no add any { } after while (ADCON0.GO_DONE == 1);

Code:
after changes :


unsigned int ADC_read(unsigned char channel)
// Si message d'erreur "'ADC_read' Identifier redefined" pendant compilation, dans Library Manager décocher ADC_Read.
// ADC_read est ma propre routine écrite pour apprentissage. Je n'utilise pas ADC_Read de la librairie Mikroelektronika.
{
static unsigned int k;                   // Variable locale.
ADCON0 = 0x41 | (channel<<2);     // ADCON0 = 01000001
                                   //        & 11000011 (masque)
                                   // ADCON0 = 01000001
                                   // masque :
                                   // b0 = 1 (ADON)                               : pour prendre en compte l'état du convertisseur A/D, en service (0) ou à l'arrêt (1).
                                   // b1 = 1 (GO/DONE)                            : pour prendre en compte l'état de la conversion A/D, en cours (1) ou terminée (0).
                                   // b2 = b3 = b4 = b5 = 0 (CHS0 CHS1 CHS2 CHS3) : pour re initialiser au premier canal de conversion, le canal 0 (CHS0 = CHS1 = CHS2 = CHS3 = 0).
                                   // b6 = b7 = 1 (ADSC0 ADSC1)                   : pour prendre en compte la valeur du diviseur, de la vitesse de conversion, choisi.
                                   // Décalage channel de 2 bits à gauche pour placer la valeur de channel dans bits b2 b3 b4 b5 (CHS0 CHS1 CHS2 CHS3).
Delay_us(100);                      // Délais de 2 mS minimum. >>> A ajuster si besoin. <<<
ADCON0.GO_DONE = 1;               // Déclenchement de la conversion A/N. via ADCON0.b2 = 1;
_asm NOP;                         // Recommandé par Microchip.
while (ADCON0.GO_DONE == 1);      // Attendre que le bit GO.DONE passe à 0.
k = ADRESH <<8;  
k=  k | ADRESL;
return(k);
}

i think, it was working because used the ADC_READ() from ADC library, not yours !
i use Compiler Output Option Case sensistive checked !

i will test this tomorow ...

the idea is to use only timer and interrupt to built the 2 Servomotor signals
Only ADC reading in main loop ..
 

Attachments

  • _16F877_2servomoteurs_rev1.zip
    6.7 KB · Views: 83

    Eric_O

    Points: 2
    Helpful Answer Positive Rating
Updated project attached.
Code has not changed from post #8 but the erroneous commenting has been removed and better commenting added.
There are still errors in timing calculations, but closer than before, where comments were clearly wrong.
The end result is good, as shown on oscilloscope. It is my maths and/or logic that is dodgy.
 

Attachments

  • ServoFromADCx2.zip
    27.6 KB · Views: 80

hello ,

At least, my idea to use Timer2 for 20mS synchro, Timer0 8 bits for Lower servo, Timer1 for upper servo
could not achieve because of the 8bits only for Timer0...
it could be OK with a Timer3 16 bits ..like on PIC18F !
Treatment inside interrupt used too long (time) compare to a 10µS interrupt to get 250 steps for
a range of 1 to 2mS in 100 steps

so , Hexreader, i tested your code ! (forum.zip)

(y)your algorhytme is very interessant

with my SQA analyser
i get this relation ship between ADC value and Pluse Duration

measures with SQA analyser
PWM1_high_time Pulse duration
16 630 µS
32 759 µS
---------- usefull range , 128 step for 270° -----
64 1.034 mS
128 1.547 ms ----- center position
192 2.066 ms
--------------------------------
Servo_pulses_with_max_ADC_values.jpg

224 2.318 ms

Servo_pulses_with_max_ADC_values_every_20ms.jpg
Servo_pulses_with_minima_ADC_values.jpg




i will test your new version with SQA analyser ...
 
Last edited:

Hi,

with an input clock of about 50kHz ...125kHz an 8 bit timer should work to generate 1ms ... 2ms pulses with good resolution

Klaus
 

i will test your new version with SQA analyser ...


it was not running .. no interrupts detected
i am using MikroC 7.60
syntaxe interrupt could be modified by

i use also internal FOSC 4MHZ ( because no Quartz)

Code:
void Interrupts() iv 0x0004 ics ICS_AUTO
//void Interrupt()
{
  unsigned int TMR1_value;                                                      // temporary calculation value
  if ( (TMR0IE_bit==1) &&  (TMR0IF_bit)  )
  ... etc

else same results as using the previous version in "forum.zip"


KlausST said:
with an input clock of about 50kHz ...125kHz an 8 bit timer should work to generate 1ms ... 2ms pulses with good resolution

possible, but the resolution is less :
100Khz Timer0 1ms=> 231 2mS=> 206 => range in 25 steps
125Khz Timer0 1ms=> 194 2ms => 131 => range in 63 steps
500Khz timer0 1ms=> 131 2ms =>6 => range in 85 steps
but also same FOSC for the CPU ... and other task

instead of 128 steps for the range 1 to 2mS (with the previous solution)
 
Last edited:

I too use latest mikroC version 7.60. What can I say? ...

Works perfectly for me with real hardware (easyPIC7 jumpered to 5V), real 4 MHz crystal, hobby servo, 2 potentiometers, external PSU for servo.

Tested and proven in my environment :(

Do you have a function generator that can feed 4MHz square wave of 0v to 5V to OSC input? - or even sine wave would probably work (offset for 0-5V). This way you could load hex file exactly as supplied.

Or maybe just wait to see how OP gets on?
 
Last edited:

hello,

i only have a 16F887 on a bread board, and power supply 4,8V
i was obliged to choose internal FOSC, because no 4 Mhz Quartz at dsiposal.
i am sure about the frequency, because i am using UART 19200 bauds..
my config bits
/CONFIG1 : $2007 : 0x2CF4
//CONFIG2 : $2008 : 0x0700

i don't have any servomotors connected ... only load the output RD0 and RD1 with pull up resistor 4,7K .
to connect my SQA anlalyser.

compiler Output config
Optimization :level : Four
case sensitive

I don't think the use of internal FOSC is linked with that.....
I encoutered also, this problem when using my old programs with 16F serie..

so, better to wait Eric's results ..
 

Attachments

  • 16F887_FOSC_interne_4Mhz.cfgsch.txt
    2 KB · Views: 91

Hi,

100Khz Timer0 1ms=> 231 2mS=> 206 => range in 25 steps
I don´t understand. 100kHz means 100 pulses per ms. thus from 1ms to 2ms there should be 100 pulses of resolution.

**

but since the pulses are in series you may use one 16 bit timer/counter for both servos.
and a flag/variable that tells the ISR which channel to process.


Klaus
 

For paulfjujo...

I am very bored right now, so I made same project, but using your project settings (internal 4MHz osc) - attached

Tested with scope traces and with hobby servo - works well.

Gives accurate 0.5ms to 2.5ms high from 0-5V ADC input - for full range servo operation - 20ms repetition rate. Two channels.

High pulse is a fraction less than 7-bits (128 count) resolution on RD0, a fraction less than 8-bits resolution on RD1.

Do not understand why it does not work for you.
 

Attachments

  • ServoFromADCx2intosc.zip
    27.9 KB · Views: 84

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top