-
First implementation
10/29/2016 at 16:11 • 11 commentsMy first version went quite well and I didn't have many surprises.
First, let's talk about software.
The Raspberry Pi's SoC doesn't allow you to write a value to a given pin. Instead, you have to write bits to a certain register to set the corresponding pin(s) to 1, and to another register to reset to 0. This is intended to remove some problems with a heavily shared I/O pins and avoid race conditions with Read/Modify/Write cycles.
Other platforms (MIPS-based PIC32, others?) have a third register to "toggle" the pin, but the BCM2835 doesn't. Damnit.
I wanted to write something elegant, with no IF, so I used a different approach. Since the pin's state must alternate, the register number will alternate too. Each time the pin is toggled, the variable that contains register number is then XORed with the difference between the SET and CLEAR registers.
The output toggling should also be enabled or disabled. A second variable is created: it is cleared when the output is disabled (no toggling because x^0=x) and it is set to the difference ("delta") for toggling the input.
(note: the following code uses my #C GPIO library for Raspberry Pi)
/* Include my personal GPIO library */ #define PI_GPIO_ERR // includes errno.h #include "PI_GPIO.c" // includes stdio.h, // sys/mman.h, fcntl.h, stdlib.h - signal.h // number of the toggled pin #define PI_OUT_MOSFET (7) /* Toggle : alternate the pointer from RPI_GPSET0 to RPI_GPCLR0 */ #define TOGGLE_DELTA ( RPI_GPSET0 ^ RPI_GPCLR0 ) int toggle_var=RPI_GPCLR0, // start with pin OFF toggle_en=0; ... // interruptible wait (5ms) in the main loop nanosleep(&nanosleeptime,NULL); // Toggle the GPIO *(PI_gpio+toggle_var)= (1 << PI_OUT_MOSFET); toggle_var^=toggle_en; // to disable toggling: toggle_en=0; // to enable toggling: toggle_en=TOGGLE_DELTA; ...
My code already contains a very short, interruptible delay (5ms) which creates a pretty good (slightly jittery) 100Hz signal on the selected pin.
Jitter is not a problem here. At least I have an approximate frequency range so I can tune the rest of the circuit.
Now, the hardware.
I checked the program's behaviour with the scope then proceeded to assemble the charge pump.
I used the same part as the following schematic:
The discharge time constant is approximately 1µ×1M=1s. This means that the same charge as C2 should be reinjected several times per second, to keep its nominal voltage.
C1 is chosen smaller to reduce the strain/current on the GPIO pin, and also because at least 10 cycles are needed to charge C2. Actually it's more like 30 to reach a good voltage, so 30Hz is like a minimum frequency. I have a 3:1 margin, that's good. The CPU can miss a few beats, and the relay has some hysteresis so the whole will not oscillate wildly in case of high CPU load.
For the beginner, the interesting trick is that the ratio of capacity between C1 and C2 deternmines the speed of rising voltage. It's also influenced by the signal frequency. A larger C1 will charge C2 faster but also create higher currents. A higher input frequency requires a smaller capacitor.
Discharge curve:
The discharge is slow and no freewheel diode is required for the relay :-)
Charge curve:
As expected, 10 cycles bring the level to 2/3Vcc (2V). At 30 cycles, it's "almost there".
The curve at the intermediary node:
Now the only problem is that the BS170 barely lets current flow at Vgs=2.90.
I have used BAT85 (small signal Schottky diodes) to minimize the losses but this is yet not enough. The BS170 would rather have 3.3V or 5V to switcth the required 65mA with the least losses possible.
(Note: "just for safety" I left the freewheel diode from the previous iterations of the board, which has seen some serious work already)
The normal solution is to wire 2 more diodes and capacitors to double the charge pump's voltage:
But space was too tight so I used the other solution: get a better transistor. I could have some use for @DeepSOIC's depletion trick but he saw that the effect doesn't last :-P
I settled for a SI4920 (double N-MOSFET in SOIC-8) that was waiting in a drawer. The behaviour is better but in case of inactivity, this leaves only 1/2s until the relay is released again.
With the new transistor (and after a few tests) I installed the board in the definitive control box.
Come near the Stade de France in Saint Denis and see it at work ;-)