void initADC(void)
{
AD1PCFG = 0xFFFF; // All pins digital
AD1CON1 = 0x00E0; // Auto-convert mode
AD1CSSL = 0x0000; // No scanning required
AD1CON2 = 0x0000; // No scanning required
AD1CON3 = 0x1F00; // Tad = 32 PBCLK cycles
AD1CON1bits.ADON = 1; // Turn on ADC
}
void initOLED(void)
{
// Initialize SPI1 module
SPI1CONbits.ON = 0; // Disable SPI module
SPI1CONbits.MSTEN = 1; // Set as Master mode
SPI1CONbits.CKE = 1; // Clock edge select
SPI1CONbits.SMP = 0; // Input data sampled at middle of data output time
SPI1CONbits.MODE16 = 0; // 8-bit mode
SPI1CONbits.MODE32 = 0; // 8-bit mode
SPI1CONbits.DISSDI = 0; // Enable SDI pin
SPI1CONbits.DISSDO = 0; // Enable SDO pin
SPI1CONbits.SSEN = 0; // SS pin not used
SPI1CONbits.ENHBUF = 1; // Enable enhanced buffer mode
SPI1CONbits.MCLKSEL = 0;// Set PBCLK as SPI clock
SPI1BRG = 0; // Set clock divider to 1
SPI1CONbits.ON = 1; // Enable SPI module
// Initialize OLED module
TRISDbits.TRISD9 = 0; // Set RD9 as output (data/command pin)
TRISGbits.TRISG6 = 0; // Set RG6 as output (reset pin)
PORTGbits.RG6 = 1; // Set OLED reset pin high
DelayMs(1); // Wait for OLED to stabilize
PORTGbits.RG6 = 0; // Set OLED reset pin low
DelayMs(10); // Wait for OLED to reset
PORTGbits.RG6 = 1; // Set OLED reset pin high
// Initialize OLED display
WriteOLEDCommand(0xAE); // Display off
WriteOLEDCommand(0xA1); // Segment remap
WriteOLEDCommand(0xDA); // Common pads hardware: alternative
WriteOLEDCommand(0x12);
WriteOLEDCommand(0xC8); // Common output scan direction:com63~com0
WriteOLEDCommand(0D5,D4 display multiplex, D3 disable to display clock, D2~D0normal)
WriteOLEDCommand(0x81); // Contrast control
WriteOLEDCommand(0xCF);
WriteOLEDCommand(0xD9); // Set pre-charge period
WriteOLEDCommand(0xF1);
WriteOLEDCommand(0xDB); // VCOM deselect level mode
WriteOLEDCommand(0x40);
WriteOLEDCommand(0xA4); // Set display mode: Reset
WriteOLEDCommand(0xA6); // Set display mode: Normal display
WriteOLEDCommand(0xAF); // Display on
}
void plotWaveform(void)
{
int i;
// Sample analog inputs
AD1CON1bits.SAMP = 1; // Start sampling
DelayUs(10); // Wait for sampling to complete
AD1CON1bits.SAMP = 0; // Stop sampling and start conversion
while (!AD1CON1bits.DONE); // Wait for conversion to complete
int adcValues[4];
adcValues[0] = ADC1BUF0;
adcValues[1] = ADC1BUF1;
adcValues[2] = ADC1BUF2;
adcValues[3] = ADC1BUF3;
// Convert ADC values to waveform buffer
for (i = 0; i < 128; i++)
{
int sum = 0;
int j;
for (j = 0; j < 4; j++)
{
AD1CHSbits.CH0SA = j; // Select analog input channel
AD1CON1bits.SAMP = 1; // Start sampling
DelayUs(10); // Wait for sampling to complete
AD1CON1bits.SAMP = 0; // Stop sampling and start conversion
while (!AD1CON1bits.DONE); // Wait for conversion to complete
sum += ADC1BUF0;
}
waveformBuffer[i] = sum / 4;
}
// Display waveform on OLED
WriteOLEDCommand(0xAE); // Display off
WriteOLEDCommand(0x20); // Set memory addressing mode
WriteOLEDCommand(0x10); // Set horizontal addressing mode
WriteOLEDCommand(0xB0); // Set page start address
WriteOLEDCommand(0x00); // Set lower column address
WriteOLEDCommand(0x10); // Set higher column address
for (i = 0; i < 128; i++)
{
WriteOLEDData(waveformBuffer[i]);
}
WriteOLEDCommand(0xAF); // Display on
}
void WriteOLEDCommand(unsigned char command)
{
LATDbits.LATD9 = OLED_CMD;
while (SPI1STATbits.SPITBF); // Wait for TX buffer to be empty
SPI1BUF = command; // Send command
while (!SPI1STATbits.SPIRBF);// Wait for RX buffer to be full
char dummy = SPI1BUF; // Discard received data
}
void WriteOLEDData(unsigned char data)
{
LATDbits.LATD9 = OLED_DATA;
while (SPI1STATbits.SPITBF); // Wait for TX buffer to be empty
SPI1BUF = data;
while (!SPI1STATbits.SPIRBF);// Wait for RX buffer to be full
char dummy = SPI1BUF; // Discard received data
}
void DelayUs(unsigned int us)
{
// Convert us to timer ticks
us *= PBCLK / 1000000;
// Use Timer 2 to generate delay
T2CONbits.TCKPS = 0; // Set prescaler to 1
T2CONbits.T32 = 0; // Timer 2 operates as two 16-bit timers
T2CONbits.TCS = 0; // Use internal clock
TMR2 = 0; // Reset timer
PR2 = us; // Set period
T2CONbits.TON = 1; // Turn on timer
// Wait for timer to expire
while (!IFS0bits.T2IF);
T2CONbits.TON = 0; // Turn off timer
IFS0bits.T2IF = 0; // Clear interrupt flag
}
int main(void)
{
TRISB = 0xFFFF; // Set all PORTB pins as inputs
TRISD = 0; // Set all PORTD pins as outputs
LATD = 0; // Clear all PORTD outputs
// Configure SPI1 for OLED communication
SPI1CONbits.ON = 0; // Turn off SPI1
SPI1CONbits.MSTEN = 1; // Master mode
SPI1CONbits.CKP = 0; // Idle clock low
SPI1CONbits.CKE = 1; // Data changes on falling edge
SPI1CONbits.SMP = 0; // Sample data on rising edge
SPI1CONbits.MODE16 = 0; // 8-bit mode
SPI1CONbits.MODE32 = 0; // 8-bit mode
SPI1CONbits.DISSDO = 0; // SDO1 enabled
SPI1CONbits.DISSDI = 1; // SDI1 disabled
SPI1CONbits.SSEN = 0; // SS1 pin not used
SPI1CONbits.ENHBUF = 0; // Standard buffer mode
SPI1CONbits.MCLKSEL = 0; // PBCLK used as clock source
SPI1BRG = 0; // Maximum baud rate
SPI1CONbits.ON = 1; // Turn on SPI1
// Configure Timer 2 for delay function
T2CONbits.ON = 0; // Turn off Timer 2
T2CONbits.TCKPS = 0; // Set prescaler to 1
T2CONbits.T32 = 0; // Timer 2 operates as two 16-bit timers
T2CONbits.TCS = 0; // Use internal clock
TMR2 = 0; // Reset timer
PR2 = 0xFFFF; // Set period to maximum
T2CONbits.ON = 1; // Turn on Timer 2
// Configure analog inputs for ADC
AD1PCFG = 0xFFFF; // Set all pins as digital
AD1CON1bits.ADON = 0; // Turn off ADC1
AD1CON1bits.FORM = 0; // Integer format
AD1CON1bits.SSRC = 0b111; // Auto-
AD1CON1bits.ASAM = 1; // Auto-start sampling
AD1CON2bits.VCFG = 0; // Use AVDD and AVSS as reference
AD1CON2bits.CSCNA = 1; // Scan inputs
AD1CON2bits.SMPI = 3; // Interrupt after 4 conversions
AD1CON2bits.BUFM = 0; // Use 16-word buffer
AD1CON2bits.ALTS = 0; // Always use MUXA
AD1CON3bits.ADRC = 0; // Use PBCLK as clock source
AD1CON3bits.SAMC = 0b11111; // 31 Tad
AD1CON3bits.ADCS = 1; // Set Tad to 2x Tpb
AD1CHSbits.CH0NA = 0; // Use Vref- as negative input
AD1CON1bits.ADON = 1; // Turn on ADC1
// Configure Timer 3 for PWM output
T3CONbits.ON = 0; // Turn off Timer 3
T3CONbits.TCKPS = 0; // Set prescaler to 1
T3CONbits.TCS = 0; // Use internal clock
T3CONbits.TGATE = 0; // Disable gated mode
T3CONbits.TSIDL = 0; // Continue operation in idle mode
T3CONbits.TSYNC = 0; // Do not sync external clock input
TMR3 = 0; // Reset timer
PR3 = 3999; // Set period to 4ms
OC1R = 0; // Clear duty cycle
OC1RS = 2000; // Set initial duty cycle to 50%
OC1CONbits.OCTSEL = 1; // Use Timer 3 for output compare
OC1CONbits.OCM = 0b110; // PWM mode
T3CONbits.ON = 1; // Turn on Timer 3
// Wait for ADC conversion to complete
while (!IFS0bits.AD1IF);
// Read ADC result and display on OLED
int i;
for (i = 0; i < 4; i++)
{
char str[10];
sprintf(str, "%04d", ADC1BUF[i]);
DrawString(10 + i * 60, 20, str, WHITE, BLACK);
}
// Update PWM duty cycle based on ADC value
OC1RS = ADC1BUF[0] / 4;