I'm looking for help regarding the use of an ADC value to control a PWM duty cycle. I require a 1kHz frequency PWM signal with two outputs and one of the two inverted. The duty cycle is varied using a potentiometer attached to an ADC channel. I have the code working to generate the signals as well as vary the duty cycle with the potentiometer. The only problem is that the duty cycle stops quite a bit short of 100%. It does however go down close to 0%. Am I doing something wrong? Here is all my code:
/*The circuit
* One LED on PB6 (this is the inverted signal)
* One LED on PB7
* Potentiometer output connected to PB5
*/
#include "driverlib/pin_map.h"
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_gpio.h"
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/pin_map.h"
#include "driverlib/gpio.h"
#include "driverlib/pwm.h"
#include "driverlib/adc.h"
#include "driverlib/fpu.h"
#define FREQUENCY 1000 //1kHz
void delayMS(int);
void init_FPU(void);
void init_PWM(void);
void init_sysCtl(void);
void init_Pins(void);
void init_ADC(void);
void set_Duty(uint32_t);
uint32_t timer_count =0;
int main(void)
{
uint32_t analogData =0;
//initialise peripherals
init_sysCtl();
init_Pins();
init_ADC();
init_PWM();
init_FPU();
delayMS(1000);
while(1)
{
ADCProcessorTrigger(ADC0_BASE,0);
while(!ADCIntStatus(ADC0_BASE, 0, false)){} //wait for adc to finish conversion
ADCSequenceDataGet(ADC0_BASE, 0, &analogData); //store the ADC data
set_Duty(analogData); //change the duty cycle
}
}
void init_FPU(void){
FPULazyStackingEnable();
FPUEnable();
}
void init_sysCtl(void){
//Set the clock
SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
//Configure PWM Clock divide system clock by 1 (Higher resolution)
SysCtlPWMClockSet(SYSCTL_PWMDIV_1);
// Enable the peripherals used by this program.
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); //Enable PortB
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0); //Enable PWM module 0
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); //Enable ADC0
}
void init_Pins(void)
{
//Configure PB6,PB7 Pins as PWM
GPIOPinConfigure(GPIO_PB6_M0PWM0);
GPIOPinConfigure(GPIO_PB7_M0PWM1);
GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_6 | GPIO_PIN_7);
//Configure PB5 as ADC_Ch11
GPIOPinTypeADC(GPIO_PORTB_BASE, GPIO_PIN_5);
}
void init_PWM(void)
{
uint32_t PWM_clock;
PWM_clock = SysCtlClockGet();
timer_count = ((PWM_clock)/(FREQUENCY))-1; //set the count value
PWMGenConfigure(PWM0_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);
PWMOutputInvert(PWM0_BASE, PWM_OUT_0_BIT,true);
//Set the Period (expressed in clock ticks)
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, timer_count);
//Set PWM duty cycle so that both outputs are close to 0 at startup
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0,timer_count-1); //inverse PWM, therefore need close to 100% for 0 output
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1,1);
// Enable the PWM generator
PWMGenEnable(PWM0_BASE, PWM_GEN_0);
// Turn on the Outputs
PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT, true);
}
void set_Duty(uint32_t data)
{
uint32_t dutyCycle = 0;
double scaleFactor = ((timer_count)/4096); // (period/max_ADC_value)
if (data <= 5)// to ensure duty cycle is never exactly 0%
{
data=5;
}
dutyCycle = (int)data*scaleFactor;
//set the duty cycles to be the same. PWM0 is automatically the inverse of PWM1
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_0, dutyCycle);
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1, dutyCycle);
}
void init_ADC(void){
ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_PROCESSOR, 0);
ADCSequenceStepConfigure(ADC0_BASE, 0, 0,ADC_CTL_IE | ADC_CTL_END | ADC_CTL_CH11); //Enable ADC on PB5 (CH11)
ADCSequenceEnable(ADC0_BASE, 0);
}
void delayMS(int ms) {
SysCtlDelay( (SysCtlClockGet()/(3*1000))*ms ) ; //rudimentary delay
}