Can't initialize PWM on dsPIC33F - c

I'm probably just having a can't-see-the-forest-for-the-trees moment with one of these registers, but I can't get the PWM working on the dsPIC33FJ32MC102 microcontroller (warning: big PDF) I'm playing around with. I've followed both the datasheet and further application note (warning: another PDF) and even code samples and I can't see what I'm doing wrong, though on my testbench I'm getting Vcc on the high output and Ground on the low output. I've tied the fault pins both to Vcc and disabled the register keycode so my changes should see some effect. What am I doing wrong?
#define FOSC (3686400ULL)
#define FCY (FOSC/2)
#include <xc.h>
#include <libpic30.h>
...
#pragma config PWMPIN = ON // Motor Control PWM Module Pin Mode bit (PWM module pins controlled by PORT register at device Reset)
#pragma config PWMLOCK = OFF
...
void main(void){
...
//setup PWM
//Clear faults
IFS3bits.PWM1IF = 0;
IFS3bits.FLTA1IF = 0;
IFS4bits.FLTB1IF = 0;
//Setup dead times
P1DTCON1bits.DTAPS = 0b00; //Dead time tick is 1 TCY
P1DTCON1bits.DTBPS = 0b00;
P1DTCON1bits.DTA = 10; //Dead time is 10TCY ~= 3uS
P1DTCON1bits.DTB = 10;
P1DTCON2bits.DTS1A = 0; //Active and Inactive transition dead times
P1DTCON2bits.DTS2A = 0; //0 takes A dead time
P1DTCON2bits.DTS3A = 0; //1 takes B dead time
P1DTCON2bits.DTS1I = 1;
P1DTCON2bits.DTS2I = 1;
P1DTCON2bits.DTS3I = 1;
P1TCONbits.PTOPS = 0b0000; //1 CPU tick = 1 PWM tick
P1TCONbits.PTCKPS = 0b00;
P1TCONbits.PTMOD = 0b00;
P1TCONbits.PTSIDL = 0; //Run when CPU idles
// no longer necessary since I disabled register write lock:
// __builtin_write_PWMSFR(&P1FLTACON,0x0000,&PWM1KEY);
// __builtin_write_PWMSFR(&P1FLTBCON,0x0000,&PWM1KEY);
// __builtin_write_PWMSFR(&PWM1CON1 ,0x0077,&PWM1KEY);
PWM1CON1 = 0x0077; //Enable all 3 channels
P1FLTACON = 0x0000; //Disable faults
P1FLTBCON = 0x0000;
//Setup Wave freq/duty
//Fosc = 7.3728 MHz -> Fcy = 3.6864MHz
//Desire a PWM of 20250Hz (smaller scalar error than 20kHz)
//P1TPER = [Fcy/(Fpwm*Scalar)] - 1
//Therefore P1TPER = [3.6864M/(20250*1)] - 1 = 181;
P1TPER = 181;
P1DC1 = 0x7FFF; // 0x7FFF for 50%
P1DC2 = 0x7FFF;
P1DC3 = 0x7FFF;
P1OVDCON = 0x3F00; //Disable override; override disables PWM
PWM1CON2 = 0x0000;
P1TCONbits.PTEN = 1; //Turn on
...
while(1);
}

I believe you have set up the timer to count from 0 to 181 (P1TPER) and then reset and repeat. But you have set the duty cycle registers to 0x7FFF, which is greater than 181. So I believe the duty cycle value will never be less than the timer value and therefore the output will never change. Try setting the duty cycle registers to 181/2 = 90 to get a duty cycle of 50%.

Related

How to properly set the SysTick Timer?

I'm having trouble generating specific time for the STM32F103C8 (Blue Pill). Apparently, the AHB main clock is set to 72 MHz. However, regardless of whether the SysTick clock source is AHB or AHB/8, the time always turns out to be 10 times longer.
Clock config
void delay(){
SysTick->LOAD = 7199999;
SysTick->CTRL = 0x05;
while((SysTick->CTRL&(1<<16)) == 0);
SysTick->CTRL = 0x00;}
This delay should be 0.1 sec. But it always works in 1 sec. Other values ​​are also 10 times higher, regardless of whether CLKSOURCE is AHB or AHB/8.
If anyone can help, I appreciate it.
Below is how I use the Systick timer:
void delaySysTicks(uint32_t msDelay) {
for(uint32_t c = 0; c < msDelay; c++) {
SysTick->CTRL = 0x0; // disable systick
SysTick->LOAD = (SystemCoreClock / 1000U); // count down reload - 1ms
SysTick->VAL = 0;
SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); // wait for timer rollover
}
}
delaySysTicks(1000); // busy wait 1 second

Timer1 on PIC24F16KA102 don't work

I want to configure timer1 of PIC24F16KA102 to count it. The clock source must be the internal clock of 8 MHz. I configured the register T1CON and set on high level the bit TON to start the timer. Timer1 is set to go in overflow every 100 us, then with a while cycle I wille increase the variable count. I'am not understanding because timer1 don't work, I observed that it does not increase. Why?
#include <xc.h>
#include "config.h"
int count = 0;
void main(void) {
TRISB = 0;
T1CON = 0; //TRM1 stopped, internal clock source, prescaler 1:1
_TON = 1;
TMR1 = 65135; //overflow of TM1 every 100 us (400 counts)
while (1) {
if (TMR1 == 65535) {
count++; // increase every 100 us
TMR1 = 65135;
}
}
}
Try setting the Timer 1 period register (PR1) and using an interrupt rather than trying to catch and reload TMR1 on its final count. You're trying to catch TMR1 on EXACTLY 65535, and that will almost never work because once TMR1 hits 65535, it's just going to overflow and begin counting from 0 again.
EDIT: Of course, this assumes it counts at all. I don't know what the behavior of a timer is when you leave the period register at 0. It may simply count to it's maximum of 65535 then reset to 0, or it may never count at all and continuously load PRx into TMRx since they match at 0
PRx is meant to define the period you want for a given timer, in this case 100uS. PR1 = 400. Once TMR1 = PR1, the timer will reset itself automatically and raise an interrupt to alert you that the timer has elapsed.
volatile unsigned int count = 0; //Vars that change in an ISR should be volatile
PR1 = 400; //Set Period for Timer1 (100us)
T1CON = 0x8000; //Enable Timer1
IEC0bits.T1IE = 1; //Enable Timer1 Interrupt
IPC0bits.T1IP = 0b011;
Pair this with an ISR function to increment count whenever the timer elapses:
void __attribute__ ((interrupt,no_auto_psv)) _T1Interrupt (void)
{
count++;
IFS0bits.T1IF = 0; //Make sure to clear the interrupt flag
}
You could also try something like this without any interrupts:
void main(void){
unsigned int count = 0;
TMR1 = 0;
T1CON = 0x8000; //TON = 1
while(1){
if (TMR1 >= 400){
count++;
TMR1=0;
}
}
}
However I would recommend using the PR register and an ISR. This is what it's meant to do.
EDIT: I would also recommend reading the PIC24F Reference Manual on timers:
Here

Embedded C code to control a DC motor with a PIC microcontroller

Im trying to create an embedded c code to control a dc motor with the PIC32MX460F512L microcontroller. Ive Configured the system clock at 80MHz, and the peripheral clock at 10MHz, Am using Timer 1 for pulsing the PWM with a given duty cycle, and Timer 2 for measuring the motor run time. I have a header file(includes.h) that contains system configuration information eg clock. Ive created most of the functions but some are a bit challenging. For example, initializing the LEDS and the functions for forward, backward movements and stop, I wanted the dc motor to run in forward direction for 4 sec at 70% duty cycle, then stop for 1 sec then reverse for 3 sec at 50% duty cycle and then stop for 1 sec and then forward again for 3 sec at 40% duty cycle, stop for 1 sec and finally forward for 5 sec at 20% duty cycle. Any suggestions for the forward, stop, and reverse functions
#include <stdio.h>
#include <stdlib.h>
#include <includes.h>
void main()
{
// Setting up PIC modules such as Timers, IOs OCs,Interrupts, ...
InitializeIO();
InitializeLEDs();
InitializeTimers();
while(1) {
WaitOnBtn1();
Forward(4.0,70);
Stop(1.0);
Backward(3.0,50);
Stop(2);
Forward(3.0,40);
Stop(1.0);
Backward(2.0,20);
LEDsOFF();
}
return;
}
void InitializeIO(){
TRISAbits.TRISA6 = 1;
TRISAbits.TRISA7 = 1;
TRISGbits.TRISG12 = 0;
TRISGbits.TRISB13 = 0;
LATGbits.LATB12 = 0;
LATGbits.LATB13 = 0;
return;
}
void InitializeLEDs(){
//code to initialize LEDS
}
void InitializeTimers(){
// Initialize Timer1
T1CON = 0x0000; // Set Timer1 Control to zeros
T1CONbits.TCKPS=3; // prescale by 256
T1CONbits.ON = 1; // Turn on Timer
PR1= 0xFFFF; // Period of Timer1 to be full
TMR1 = 0; // Initialize Timer1 to zero
// Initialize Timer2
T2CON = 0;
T2CONbits.TCKPS = 7; // prescale by 256
T2CONbits.T32 = 1; // use 32 bits timer
T2CONbits.ON = 1;
PR2 = 0xFFFFFFFF; // Period is set for 32 bits
TMR2 = 0;
}
void WaitOnBtn1(){
// wait on Btn1 indefinitely
while(PORTAbits.RA6 == 0);
// Turn On LED1 indicating it is Btn1 is Pushed
LATBbits.LATB10 = 1;
return;
}
void Forward(float Sec, int D){
int RunTime = (int)(Sec*39000); // convert the total
time to number of Tics
TMR2 = 0;
//LEDs
LATGbits.LATG12 = 1; // forward Direction
LATBbits.LATB12 = 0;
LATBbits.LATB13 = 0;
LATBbits.LATB11 = 1;
// Keep on firing the PWM as long as Run time is not
elapsed
while (TMR2 < RunTime){
PWM(D);
}
return;
}
void PWM(int D){
TMR1 = 0;
int Period = 400;
while (TMR1< Period) {
if (TMR1 < Period*D/100){
LATGbits.LATG13 = 1;
}
else{
LATGbits.LATG13 = 0;
}
}
Functions, not methods, to be precise.
So what is exactly the question?
What I can say from a quick look on a source code:
LEDs initialisation should be done as you did in InitializeIO() function. Simply set proper TRISx bits to 0 to configure LED pins as output.
For the PWM and motor control functions you should take some time and try to understand how builtin PWM peripheral works. It is a part of OC (Output Compare) and it is very easy to use. Please, take look on following link http://ww1.microchip.com/downloads/en/DeviceDoc/61111E.pdf
and this one for the minimal implementation using builtin peripheral libraries https://electronics.stackexchange.com/questions/69232/pic32-pwm-minimal-example
Basically you need to set up OC registers to "make" OC module acts like PWM. You need to allocate one of the timers to work with OC module (it will be used for base PWM frequency) and that's it.
All you need after that is to set PWM duty cycle value by setting timer PRx register, you don't need to swap bits like in your PWM routine.
To stop it simple stop it simply disable the timer.
To run it again run the timer.
To change direction (it depends of your driver for the motor) I guess you need just to toggle direction pin.
I hope it helps...

CCP module - PWM mode

I'm trying to make a PWM output with this code, but apparently something is missing. Because when I check the Logic Analyzer in MPLab, nothing happens on CCP2 output. I'm working with the pic18f25k80.
void main() {
// Set up PWM
CCP2CON = 0b00001100; // Enable PWM on CCP2, bits 5:4 are LSB part of duty cycle
CCPTMRS = 0b00000000; // Use timer2 for all CCP modules
CCPR2L = 31; // MSB part of duty cycle
TRISC = 0b00000000; // Set port C as output
PORTC = 0; // Clear port C
// Set Up timer2
PR2 = 249; // PWM period = (PR+1)*4*Tcy = 1ms
T2CON = 0b00000100; // Enable TMR2 with prescaler = 1
while(1)
{
}
}
I expect when
TMR2 = PR2, CCP2 output toggles and timer is reset
further, TMR2 = CCPR2L (duty cycle), CCP2 output toggles
TMR2 keeps counting until step 1 is reached.
I suppose this is what should happen automatically. I mean I don't have to write the code for that, because that's THE function of the PWM module, right?
What am I missing?
Additional info:
TMR 2 is counting.
When I add PORTC = 0xFF; in the while loop and debug the code again. All signals on Port C are set, except RC2 (RC2 = corresponding output of CCP2).
When I try the same code for CCP3, all signals on port C are set, except RC2 and RC6 (RC6 = corresponding output of CCP3).
When I replace PORTC = 0xFF; with PORTCbits.CCP2 = 1;, only RC1 is set high.
Does this last bullet mean that the CCP2 is muxed with RC1 instead of RC2?

variable PWM setting in PIC24FJ64GA002

I am working on project in which I need to display different colors on RGB led. I am using pwm to drive different colors on LED. My Pic is PIC24FJ64GA004 with which I am working on now. Basic concept of this project is to use switch to controls colors.
Colors on RGB led will be according to days and month in year. For that I am using 7-segment led with switch to count days and month.
Problem at the moment is following code. I am trying to change PWM values by following settings. But instead of changing it it is giving me some thing weird.
I need your help guys. Could you please help me with this thing.
for( counter=0x0000;counter<=0x4571;counter++){
//*****Timer2 starting from here*****//
PR2 = 0x4571; // Initialize PR2 with 0x4571 = 17777 as PWM cycle
IFS0bits.T2IF = 0; // Clear Output Compare interrupt flag
IEC0bits.T2IE = 1; // Enable Output Compare interrupts
T2CONbits.TON = 1; // Start Timer2 with assumed settings
//**********************************//
//*****For RED LED OC1 choosed with timer 2*****//
OC1CONbits.OCM = 0; // Output compare channel is disabled
OC1R = 0x0000 ; // Initialize Compare Register1 with 50% duty cycle
OC1RS = counter; // Initialize Secondary Compare Register1 with 50% duty cycle
OC1CONbits.OCSIDL = 0; // Output capture will continue to operate in CPU Idle mode
OC1CONbits.OCFLT = 0; // No PWM Fault condition has occurred (this bit is only used when OCM<2:0> = 111)
OC1CONbits.OCTSEL = 0; // Timer2 is the clock source for output Compare
OC1CONbits.OCM = 0x6; // PWM mode on OC, Fault pin disabled
//*****For Green Led OC2 and OC3 Choosed with timer2 as well*****//
OC2CONbits.OCM = 0; // Output compare channel is disabled
OC2R =0x0000; // Initialize Compare Register1 with 50% duty cycle
OC2RS =counter; // Initialize Secondary Compare Register1 with 50% duty cycle
OC2CONbits.OCSIDL = 0; // Output capture will continue to operate in CPU Idle mode
OC2CONbits.OCFLT = 0; // No PWM Fault condition has occurred (this bit is only used when OCM<2:0> = 111)
OC2CONbits.OCTSEL = 0; // Timer2 is the clock source for output Compare
OC2CONbits.OCM = 0x6; // PWM mode on OC, Fault pin disabled
//*****For Blue Led OC2 and OC3 Choosed with timer2 as well*****//
OC3CONbits.OCM = 0; // Output compare channel is disabled
OC3R = 0x0000; // Initialize Compare Register1 with 50% duty cycle
OC3RS = counter; // Initialize Secondary Compare Register1 with 50% duty cycle
OC3CONbits.OCSIDL = 0; // Output capture will continue to operate in CPU Idle mode
OC3CONbits.OCFLT = 0; // No PWM Fault condition has occurred (this bit is only used when OCM<2:0> = 111)
OC3CONbits.OCTSEL = 0; // Timer2 is the clock source for output Compare
OC3CONbits.OCM = 0x6; // PWM mode on OC, Fault pin disabled
}
But this code is working alright. I put different values as well. It works fine.
//*****For RED LED OC1 choosed with timer 2*****//
OC1CONbits.OCM = 0; // Output compare channel is disabled
OC1R = 0x22B8; // Initialize Compare Register1 with 50% duty cycle
OC1RS = 0x22B8; // Initialize Secondary Compare Register1 with 50% duty cycle
OC1CONbits.OCSIDL = 0; // Output capture will continue to operate in CPU Idle mode
OC1CONbits.OCFLT = 0; // No PWM Fault condition has occurred (this bit is only used when OCM<2:0> = 111)
OC1CONbits.OCTSEL = 0; // Timer2 is the clock source for output Compare
OC1CONbits.OCM = 0x6; // PWM mode on OC, Fault pin disabled
//*****For Green Led OC2 and OC3 Choosed with timer2 as well*****//
OC2CONbits.OCM = 0; // Output compare channel is disabled
OC2R =0x22B8; // Initialize Compare Register1 with 50% duty cycle
OC2RS =0x22B8;//0x22B8; // Initialize Secondary Compare Register1 with 50% duty cycle
OC2CONbits.OCSIDL = 0; // Output capture will continue to operate in CPU Idle mode
OC2CONbits.OCFLT = 0; // No PWM Fault condition has occurred (this bit is only used when OCM<2:0> = 111)
OC2CONbits.OCTSEL = 0; // Timer2 is the clock source for output Compare
OC2CONbits.OCM = 0x6; // PWM mode on OC, Fault pin disabled
//*****For Blue Led OC2 and OC3 Choosed with timer2 as well*****//
OC3CONbits.OCM = 0; // Output compare channel is disabled
OC3R = 0x22B8; // Initialize Compare Register1 with 50% duty cycle
OC3RS = 0x22B8; // Initialize Secondary Compare Register1 with 50% duty cycle
OC3CONbits.OCSIDL = 0; // Output capture will continue to operate in CPU Idle mode
OC3CONbits.OCFLT = 0; // No PWM Fault condition has occurred (this bit is only used when OCM<2:0> = 111)
OC3CONbits.OCTSEL = 0; // Timer2 is the clock source for output Compare
OC3CONbits.OCM = 0x6; // PWM mode on OC, Fault pin disabled
//*****Timer1 starting from here*****//
PR1 = 65535; // Initialize PR2 cycle
T1CONbits.TCKPS = 2; // Setting pre-scaler to 1/64
IFS0bits.T1IF = 0; // Clear Output Compare interrupt flag
IEC0bits.T1IE = 1; // Enable Output Compare interrupts
T1CONbits.TON = 1; // Start Timer1 with assumed settings
//*****Timer2 starting from here*****//
PR2 = 0x4571; // Initialize PR2 with 0x4571 = 17777 as PWM cycle
IFS0bits.T2IF = 0; // Clear Output Compare interrupt flag
IEC0bits.T2IE = 1; // Enable Output Compare interrupts
T2CONbits.TON = 1; // Start Timer2 with assumed settings
//**********************************//
You are updating your PWMs each time around the loop.
It's very unlikely that the counter has had time to expire, therefore you keep resetting it before it has chance to "do" a PWM cycle.
At the end of the loop, you need to wait for (at least one of) your PWMs to have expired.
An alternative way might be to set up the next value you want in the loop and have an interrupt service routine copy that to the PWM register when the timer expires. And once that has happened, you can set up the next next value :) You need to take care about how you transfer values between the main loop and the ISR as they are effectively different "thread" contexts.

Resources