camerart
Well-Known Member
Hi M,Did I not write that?
Mike.
Yes you did, but it had a "but" in front of it, and I wanted to make sure.
That's good, I've now to make an SMD PCB for the HC164.
C.
Follow along with the video below to see how to install our site as a web app on your home screen.
Note: This feature may not be available in some browsers.
Hi M,Did I not write that?
Mike.
Hi M,So, I did write that. The use of but was that I believe 1 chip can do everything.
Mike.
Hi M,No, just 1 microcontroller but still needs the 164.
Mike.
Yes, absolutely - for this specific application.So, is the 74HC164 the best choice?
Hi R,Yes, absolutely - for this specific application.
The idea is to shift one bit through, so it sets each output high in turn. The "flicker" if you like, but that is a sequence of servo pulses.
The clock input to shift it will come from the output compare pin so the timing is then purely hardware, and software delays cannot cause jitter - the software has plenty of time just to set up the next compare each time.
The 595 is ideal as as "output expander" for normal things like switching LEDs or relays, as you can shift data in without affecting the pins then transfer it to the latch once it's in the correct bit alignment.
It needs more control signals though so it can only be driven via a software routine, which means the likelihood of jitter again.
Hi R,In a way, yes!
#include <xc.h>
#include "config.c"
#include <stdint.h>
#define _XTAL_FREQ 32000000
#define NUM_SERVOS 10
uint16_t servoPos[NUM_SERVOS];
uint32_t ms;
uint8_t count=0,servoCount;
void main(void) {
OSCCON=0b01110000; //8MHz
PLLEN=1; //x4=32MHz
//setup 1mS interrupt = 8,000,000/16 = 500,000/10 = 50,000 set PR2=49 = 50,000/50 = 1000 = 1mS
T2CON=0b01001110; //pre=16 post=10
PR2=49;
TMR2IE=1; //timer 2 interrupts enable
T1CON=0; //timer 1 stopped
for(uint8_t i=0;i<NUM_SERVOS;i++){
servoPos[i]=i*1000+8000; //1ms(8000) to 1.875(7/8ths - 15000)ms in 1/8th mS steps
}
TRISC=0b11111100; //CCP0 & 1 output
PEIE=1;
GIE=1;
while(1){
//adjust servo positions here
}
}
void __interrupt() inter(void){
if(TMR2IE && TMR2IF){
ms++;
count++;
if(count>=20){ //start every 20mS
TMR1=0; //zero timer 1
T1CON=1; //start timer 1
count=0;
CCP1CON=0b1000; //CCP1 pin low and high on match - will be first pulse
CCPR1=2000; //start pulse in 0.25mS (2000 clock cycles)
CCP1IE=1; //enable CCP1 interrupts
CCP1IF=0; //ensure interrupt flag is clear
servoCount=0; //reset servoCount
LATC0=1; //connected to data in of shift register will clock in a high when CCP1 goes high
}
TMR2IF=0;
}
if(CCP1IE && CCP1IF){
LATC0=0; //clear the data in pin
if(servoCount==9) //have we done all servos?
CCP1IE=0; //yes so no more CCP1 interrupts
if(CCP1CON==0b1000){ //have we started the 4000 cycle pulse
CCP1CON=0b1001; //yes so end the pulse after 0.5mS
CCPR1=CCPR1+4000; //4000 cycles=0.5mS
}else{
CCP1CON=0b1000; //No so output the timed gap
CCPR1=CCPR1+servoPos[servoCount++]-4000; //will generate an interrupt when servo time is up
}
CCP1IF=0;
}
}
One possible issue with the 74HC164 is as the BITs are filling the BYTE, if they don't fill fast enough, they have a momentary voltage, I am assuming that the PIC that clocks them is too fast to worry about.
Hi R,That's not a problem, it is essential for how this setup works!
Look at the actual RC signal waveforms I posted earlier on:
View attachment 140109
The shift register moves a single bit through to each output in turn - like the "Channel x Servo signals"
The PPM waveform at the top is from the PIC compare hardware rather than a radio receiver, shifting the bit each 1 - 2 mS as appropriate for each respective servo position.
Unless the outputs changes as the SR was clocked, there would not be any servo outputs!
For decades, Radio control receivers used to use a shift reg IC for the conversion back to servo pulses, before MCU based ones took over.
ps. If you need to translate to 5V power for the shift register, it should have been a 74HCT164
The HCT ones take TTL levels and 3.3V logic is perfect to feed those with the HCT device working on 5V.
Hi M,This is code I think will work with a 164. RC0 should go to the data input (pins 1&2) and RC1 to clock (pin 8). The servos go to Qa to Qe.
Not tested as no hardware, comments welcome.
It (I hope) outputs a 0.5mS pulse every servoTime.Code:#include <xc.h> #include "config.c" #include <stdint.h> #define _XTAL_FREQ 32000000 #define NUM_SERVOS 10 uint16_t servoPos[NUM_SERVOS]; uint32_t ms; uint8_t count=0,servoCount; void main(void) { OSCCON=0b01110000; //8MHz PLLEN=1; //x4=32MHz //setup 1mS interrupt = 8,000,000/16 = 500,000/10 = 50,000 set PR2=49 = 50,000/50 = 1000 = 1mS T2CON=0b01001110; //pre=16 post=10 PR2=49; TMR2IE=1; //timer 2 interrupts enable T1CON=0; //timer 1 stopped for(uint8_t i=0;i<NUM_SERVOS;i++){ servoPos[i]=i*1000+8000; //1ms(8000) to 1.875(7/8ths - 15000)ms in 1/8th mS steps } TRISC=0b11111100; //CCP0 & 1 output PEIE=1; GIE=1; while(1){ //adjust servo positions here } } void __interrupt() inter(void){ if(TMR2IE && TMR2IF){ ms++; count++; if(count>=20){ //start every 20mS TMR1=0; //zero timer 1 T1CON=1; //start timer 1 count=0; CCP1CON=0b1000; //CCP1 pin low and high on match - will be first pulse CCPR1=2000; //start pulse in 0.25mS (2000 clock cycles) CCP1IE=1; //enable CCP1 interrupts CCP1IF=0; //ensure interrupt flag is clear servoCount=0; //reset servoCount LATC0=1; //connected to data in of shift register will clock in a high when CCP1 goes high } TMR2IF=0; } if(CCP1IE && CCP1IF){ LATC0=0; //clear the data in pin if(servoCount==9) //have we done all servos? CCP1IE=0; //yes so no more CCP1 interrupts if(CCP1CON==0b1000){ //have we started the 4000 cycle pulse CCP1CON=0b1001; //yes so end the pulse after 0.5mS CCPR1=CCPR1+4000; //4000 cycles=0.5mS }else{ CCP1CON=0b1000; //No so output the timed gap CCPR1=CCPR1+servoPos[servoCount++]-4000; //will generate an interrupt when servo time is up } CCP1IF=0; } }
Mike.
Edit, added a millisecond interrupt as it's useful.
Hi M,C can you confirm that doing CCPR1 = 0x55AA writes 0x55 to CCPR1H and 0xaa CCPR1L?
Mike.
Edit, can Ian Rogers or ericgibbs confirm this?
Write:Hi M,
I tried this test, and it looks as if CCPR1H is not being written to.
C
================================
Dim test As Word
test = 0x55aa
CCPR1 = test
=================================
Hi J,Write:
CCPR1H = test.HB
CCPR1L = test.LB
test.HB=CCPR1H
test.LB=CCPR1L
test=test+servoPos(servoCount)
test=test-4000
CCPR1H=test.HB
CCPR1L=test.LB
servoCount-servoCount+1
Hi M,For
CCPR1=CCPR1+servoPos[servoCount++]-4000; //will generate an interrupt when servo time is up
You can do,
Code:test.HB=CCPR1H test.LB=CCPR1L test=test+servoPos(servoCount) test=test-4000 CCPR1H=test.HB CCPR1L=test.LB servoCount-servoCount+1
OR, much better solution,
switch to C.
Mike.