They never arrived.
It happens with our local mob called "Post and Customs".
So I am forced to think about alternative solutions.
Porting code from PIC to AVR is an interesting task.
These days I am preparing to try it.
But I'm out of shape!
Because I have an inflamed nerve in my shoulder that produces sharp pain and interferes with my focus.
I flipped through the book again and looked at the source code that Carl provided.
I had a problem at first because Carl is a huge fan of abbreviations that don't resemble anything!
And the worst thing is when he mixes up those abbreviations and uses one in the text and the other on the schematic!
Oooooahahahaha!
Carl... another suggestion for "Errata"...
Right now I'm an "idiot" and I don't understand anything.
Here is the graph and timing.
As a dumb programmer I need a literal translation of the train of pulses into the correct order.
Source code comments didn't help me much!
Carl is "The Duke of Obfuscation"!
His works do not need additional copyright protection!
And here is the first attempt at porting the code:
(I checked on an Atmega328P, it actually works!?? But you need to organize the timings better...)
Warning!
The code is under development, NEVER try it with mosfets if you are not a fan of white smoke!
Code:
#include <Arduino.h>
#define TXP_PIN 9 // Transmit Positive Pulse (PB1)
#define TXN_PIN 10 // Transmit Negative Pulse (PB2)
#define ADC_POT A1 // Potentiometer for delay adjustment
#define TCLKP 2 // Demodulator control (PD2)
#define GCLKP 3 // Demodulator control (PD3)
#define TCLKN 4 // Demodulator control (PD4)
#define GCLKN 5 // Demodulator control (PD5)
#define BCLK 6 // Charge pump control (PD6)
#define LED_PIN 13 // Debug LED (PB5)
// Timing constants for 5kHz PPS
#define TX_PERIOD 200 // 200µs period for 5kHz PPS
#define TX_WIDTH 50 // 50µs TX pulse width
#define MAIN_DELAY 15 // 15µs delay before sampling
#define MAIN_WIDTH 10 // 10µs main sample width
#define GND_DELAY 5 // 5µs ground delay
#define GND_WIDTH 40 // 40µs ground width
volatile unsigned long pulseDelay = MAIN_DELAY; // Default delay after main pulse
void setup()
{
DDRB |= (1 << PB1) | (1 << PB2) | (1 << PB5); // Set TXP, TXN, and LED as outputs
DDRD |= (1 << PD2) | (1 << PD3) | (1 << PD4) | (1 << PD5) | (1 << PD6); // Set control pins as outputs
Serial.begin(115200);
Serial.println("Bipolar PI (5kHz PPS)");
noInterrupts();
TCCR1A = 0;
TCCR1B = (1 << WGM12) | (1 << CS10); // CTC mode, no prescaler
OCR1A = 16000; // Match value for 1ms (assuming 16MHz clock)
TIMSK1 |= (1 << OCIE1A); // Enable Timer1 interrupt
interrupts();
}
ISR(TIMER1_COMPA_vect)
{
PINB |= (1 << PB5); // Toggle LED for debug
}
void generatePulse()
{
PORTB |= (1 << PB1); // TXP HIGH
PORTB &= ~(1 << PB2); // TXN LOW
delayMicroseconds(TX_WIDTH); // TX pulse width
PORTB &= ~(1 << PB1); // TXP LOW
PORTB |= (1 << PB2); // TXN HIGH
delayMicroseconds(TX_WIDTH);
PORTB &= ~(1 << PB2); // TXN LOW
delayMicroseconds(pulseDelay); // Delay before demodulator signals
}
void controlChargePump()
{
PORTD |= (1 << PD6); // BCLK HIGH
delayMicroseconds(50);
PORTD &= ~(1 << PD6); // BCLK LOW
}
void loop()
{
generatePulse();
controlDemodulator();
controlChargePump();
delayMicroseconds(TX_PERIOD - (2 * TX_WIDTH + pulseDelay + MAIN_WIDTH + GND_DELAY + GND_WIDTH)); // Adjust cycle timing for 5kHz PPS
}
void controlDemodulator() // 25uS delay is just for testing purposes, don't be confused by this...
{
PORTD |= (1 << PD2); // TCLKP HIGH
delayMicroseconds(25);
PORTD &= ~(1 << PD2); // TCLKP LOW
delayMicroseconds(25);
PORTD |= (1 << PD4); // TCLKN HIGH
delayMicroseconds(25);
PORTD &= ~(1 << PD4); // TCLKN LOW
delayMicroseconds(25);
PORTD |= (1 << PD3); // GCLKP HIGH
delayMicroseconds(25);
PORTD &= ~(1 << PD3); // GCLKP LOW
delayMicroseconds(25);
PORTD |= (1 << PD5); // GCLKN HIGH
delayMicroseconds(25);
PORTD &= ~(1 << PD5); // GCLKN LOW
//delayMicroseconds(25);
delayMicroseconds(MAIN_WIDTH); // Demodulator pulse width
}
/*
* Reserved
* void controlDemodulator()
{
PORTD |= (1 << PD2); // TCLKP HIGH
PORTD &= ~(1 << PD4); // TCLKN LOW
PORTD &= ~(1 << PD3); // GCLKP LOW
PORTD |= (1 << PD5); // GCLKN HIGH
delayMicroseconds(MAIN_WIDTH); // Demodulator pulse width
PORTD &= ~(1 << PD2); // TCLKP LOW
PORTD |= (1 << PD4); // TCLKN HIGH
PORTD |= (1 << PD3); // GCLKP HIGH
PORTD &= ~(1 << PD5); // GCLKN LOW
}
*/
/*
* void generatePulse() {
PORTB |= (1 << PB1); // TXP HIGH
PORTB &= ~(1 << PB2); // TXN LOW
delayMicroseconds(TX_WIDTH); // TX pulse width
PORTB &= ~(1 << PB1); // TXP LOW
delayMicroseconds(3); // DEAD TIME (adjustable)
PORTB |= (1 << PB2); // TXN HIGH
delayMicroseconds(TX_WIDTH);
PORTB &= ~(1 << PB2); // TXN LOW
delayMicroseconds(3); // DEAD TIME before next cycle
}
MOSFET Switching Characteristics
IRF9640 (P-Channel)
Turn-On Delay (td(on)): ~25 ns
Turn-Off Delay (td(off)): ~100 ns
Total Switching Time: ~125 ns
IRF740 (N-Channel)
Turn-On Delay (td(on)): ~10 ns
Turn-Off Delay (td(off)): ~80 ns
Total Switching Time: ~90 ns
-P-MOSFETs (IRF9640) turn off slower (~100 ns delay)
-N-MOSFETs (IRF740) turn off slightly faster (~80 ns delay)
*************
-1 µs dead time between TXP OFF and TXN ON
-1 µs dead time between TXN OFF and next cycle
-Prevents shoot-through & protects MOSFETs
**************************************************
void generatePulse() {
PORTB |= (1 << PB1); // TXP HIGH
PORTB &= ~(1 << PB2); // TXN LOW
delayMicroseconds(TX_WIDTH); // TX pulse width
PORTB &= ~(1 << PB1); // TXP LOW
delayMicroseconds(1); // DEAD TIME (~1 µs)
PORTB |= (1 << PB2); // TXN HIGH
delayMicroseconds(TX_WIDTH);
PORTB &= ~(1 << PB2); // TXN LOW
delayMicroseconds(1); // DEAD TIME (~1 µs)
}
************************************************
*/

Leave a comment: