f = 1 / [ (PR2 + 1) x 4 x (1/16MHz) x Prescale]
(PR2 = 1, prescale = 1 => frekvens = 2 MHz)
w = PWMxDC x (1/16MHz) x Prescale (10 bit)
(PWMxDC = 800, Prescale = 1 => pulsbredd = 50 us)
dc = PWMxDC / [ 4 x (PR2 + 1) ]
(PWMxDC = 800, PR2 = 1 => duty cycle = 100)
När jag kör min testkod så ser jag något tiotal pulser (med mycket tomrum emellan) på oscilloskopet och därefter förblir utgången hög. Jag har med andra ord missförstått något i min setup, alternativt killgissat helt fel i formlerna. Det sitter en potentiometer på RA2 och min grundtanke är att justera PWM beroende på hur potentiometern är inställd (0-100%).
Koden:
Kod: Markera allt
/*
* Created on March 22, 2024, 7:19 PM
*/
// CONFIG
#pragma config FOSC = INTOSC // Oscillator Selection bits (INTOSC oscillator: CLKIN function disabled)
#pragma config BOREN = OFF // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // MCLR Pin Function Select bit (MCLR pin function is MCLR)
#pragma config CP = OFF // Code Protection bit (Program memory code protection is disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)
#pragma config LPBOR = OFF // Brown-out Reset Selection bits (BOR disabled)
#pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
#include <xc.h>
#define ADC_VALUE_MAX 255
#define ADC_VALUE_MIN 0
// (PWM_DUTY_CYCLE_MAX / ADC_VALUE_MAX)
// Max value according to formula: 800
// Max value after scaling it with ADC-value; 256 * 3 = 768
#define PWM_DUTY_CYCLE_MAX 800
#define PWM_DUTY_CYCLE_MAX_CALCULATED 768
/*
* PWM1 - RA0
* PWM2 - RA1
* POT - RA2
*/
void PWM_set_duty_cycle(unsigned char value) {
unsigned int duty_cycle;
// Value is from ADC, which is 8-bit, and needs to
// be converted into 10-bit to match the duty cycle. We
// therefore multiply the value with 3.
duty_cycle = (unsigned int) 3 * value;
if (duty_cycle > PWM_DUTY_CYCLE_MAX_CALCULATED)
duty_cycle = PWM_DUTY_CYCLE_MAX_CALCULATED;
// dc = 0b------nnnnnnnnmm
// DCH = 0bnnnnnnnn
// DCL = 0bmm------
PWM1DCH = (unsigned char) (duty_cycle >> 2);
PWM1DCL = (unsigned char) ( (duty_cycle & 0b11) << 6 );
}
void PWM_enable() {
PWM1OE = 1;
}
void PWM_disable() {
PWM1OE = 0;
}
void PWM_init() {
// Steps mentioned on page 101 in datasheet
// 1. Disable PWM1 output driver
TRISA0 = 1;
// 2. Clear PWMCON
PWM1CON = 0;
// 3. Period-value -> PR2
PR2 = 1;
// 4. Clear PWM1DCH and PWM1DCL<7:6>
PWM1DCH = PWM1DCL = 0;
// 5. Config and start Timer2
TMR2IF = 0;
T2CON = 0b00000000;
TMR2ON = 1;
// 6. Enable PWM output pin and wait for Timer2-overflow
PWM_enable();
while (!TMR2IF) {};
// 7. Enable PWM1
TRISA0 = 0;
// 8. Configure PWM1CON with appropriate values
// PWM1DC = 800 / 0x320 / 11001000 00
PWM_set_duty_cycle(ADC_VALUE_MAX);
PWM1EN = 1; // Start PWM
PWM1OE = 1; // Output Enable
PWM1POL = 0; // Active high
}
void ADC_init() {
// RA2 input for ADC, reading potentiometer
ANSELA = 0b00000100;
// Setup and enable AD-module: Fosc/16, AN2
ADCON = 0b10101001;
}
unsigned char ADC_get() {
// Start ADC
GO_nDONE = 1;
// Wait for ADC to finish
while (!GO_nDONE) {}
// Return ADC-value
return ADRES;
}
void MCU_init() {
OSCCON = 0b01110000; // 16 MHz INTOSC
TRISA = 0b11111111;
}
void main() {
unsigned char adc_value;
MCU_init();
PWM_init();
ADC_init();
PWM_enable();
while(1) {
NOP();
}
}
Finns det något uppenbart tokfel i koden och/eller formlerna?