Author Topic: CANbus with dsPIC33EV problem  (Read 1093 times)

0 Members and 1 Guest are viewing this topic.

Offline tech_builder

  • Contributor
  • Posts: 40
  • Country: ca
CANbus with dsPIC33EV problem
« on: December 12, 2018, 11:27:20 pm »
Hi,

I am trying to work with the CANbus peripheral on a dsPIC33EV256GM106 on the dsPIC33EV 5V CAN-LIN Starter Kit development board. I have been studying the peripheral for a little while now and have gone through the datasheet and various examples for this and other similar dsPIC microcontrollers. I had arrived at what I hoped was the bare code required to send a message on the CANbus; however, I am not able to get it working.
I have run the demo code that comes with the board and it works fine. When I scope the output of the transceiver I get nice signal.



When I run my code there is no activity on the output of the transceiver, only on the output from the microcontroller to the transceiver, but it is garbage. The signal shown is from the PIC to the CAN transceiver, no signal appears on the output of the CAN transceiver. I'm not sure how to account for the difference in the length of the signals or the shape.



My intention is to run the microcontroller at 16MHz, with the CANbus running at 250kbps. The  CAN parameters I am using are (register values):
Tq: 16
BRP: 0x01
PRSEG: 0x06
SEG1PH: 0x05
SEG2PH: 0x01
SJW: 0x01
Any help getting this working or wise words would be greatly appreciated. Thanks!
The code is as follows:
Code: [Select]
// DSPIC33EV256GM106 Configuration Bit Settings

// 'C' source line config statements

// FSEC
#pragma config BWRP = OFF               // Boot Segment Write-Protect Bit (Boot Segment may be written)
#pragma config BSS = DISABLED           // Boot Segment Code-Protect Level bits (No Protection (other than BWRP))
#pragma config BSS2 = OFF               // Boot Segment Control Bit (No Boot Segment)
#pragma config GWRP = OFF               // General Segment Write-Protect Bit (General Segment may be written)
#pragma config GSS = DISABLED           // General Segment Code-Protect Level bits (No Protection (other than GWRP))
#pragma config CWRP = OFF               // Configuration Segment Write-Protect Bit (Configuration Segment may be written)
#pragma config CSS = DISABLED           // Configuration Segment Code-Protect Level bits (No Protection (other than CWRP))
#pragma config AIVTDIS = DISABLE        // Alternate Interrupt Vector Table Disable Bit  (Disable Alternate Vector Table)

// FBSLIM
#pragma config BSLIM = 0x1FFF           // Boot Segment Code Flash Page Address Limit Bits (Enter Hexadecimal value)

// FOSCSEL
#pragma config FNOSC = PRIPLL           // Initial oscillator Source Selection Bits (Primary Oscillator with PLL module (XT + PLL, HS + PLL, EC + PLL))
#pragma config IESO = OFF                // Two Speed Oscillator Start-Up Bit (Start up device with FRC,then automatically switch to user selected oscillator source)

// FOSC
#pragma config POSCMD = XT              // Primary Oscillator Mode Select Bits (XT Crystal Oscillator mode)
#pragma config OSCIOFNC = ON            // OSC2 Pin I/O Function Enable Bit (OSC2 is general purpose digital I/O pin)
#pragma config IOL1WAY = OFF            // Peripheral Pin Select Configuration Bit (Allow Multiple reconfigurations)
#pragma config FCKSM = CSDCMD           // Clock Switching Mode Bits (Both Clock Switching and Fail-safe Clock Monitor are disabled)
#pragma config PLLKEN = ON              // PLL Lock Enable Bit (Clock switch to PLL source will wait until the PLL lock signal is valid)

// FWDT
#pragma config WDTPOST = PS32768        // Watchdog Timer Postscaler Bits (1:32,768)
#pragma config WDTPRE = PR128           // Watchdog Timer Prescaler Bit (1:128)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable Bits (WDT and SWDTEN Disabled)
#pragma config WINDIS = OFF             // Watchdog Timer Window Enable Bit (Watchdog timer in Non-Window Mode)
#pragma config WDTWIN = WIN25           // Watchdog Window Select Bits (WDT Window is 25% of WDT period)

// FPOR
#pragma config BOREN0 = ON              // Brown Out Reset Detection Bit (BOR is Enabled)

// FICD
#pragma config ICS = PGD2               // ICD Communication Channel Select Bits (Communicate on PGEC2 and PGED2)

// FDMTINTVL
#pragma config DMTIVTL = 0xFFFF         // Lower 16 Bits of 32 Bit DMT Window Interval (Enter Hexadecimal value)

// FDMTINTVH
#pragma config DMTIVTH = 0xFFFF         // Upper 16 Bits of 32 Bit DMT Window Interval (Enter Hexadecimal value)

// FDMTCNTL
#pragma config DMTCNTL = 0xFFFF         // Lower 16 Bits of 32 Bit DMT Instruction Count Time-Out Value (Enter Hexadecimal value)

// FDMTCNTH
#pragma config DMTCNTH = 0xFFFF         // Upper 16 Bits of 32 Bit DMT Instruction Count Time-Out Value (Enter Hexadecimal value)

// FDMT
#pragma config DMTEN = DISABLE          // Dead Man Timer Enable Bit (Dead Man Timer is Disabled and can be enabled by software)

// FDEVOPT
#pragma config PWMLOCK = ON             // PWM Lock Enable Bit (Certain PWM registers may only be written after key sequence)
#pragma config ALTI2C1 = OFF            // Alternate I2C1 Pins Selection Bit (I2C1 mapped to SDA1/SCL1 pins)

// FALTREG
#pragma config CTXT1 = NONE             // Interrupt Priority Level (IPL) Selection Bits For Alternate Working Register Set 1 (Not Assigned)
#pragma config CTXT2 = NONE             // Interrupt Priority Level (IPL) Selection Bits For Alternate Working Register Set 2 (Not Assigned)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>

#include <stdio.h>
#include <stdlib.h>

#define NUM_OF_ECAN_BUFFERS 32

void configOsc( void );
void configIO( void );
void configCAN( void );
void configDMA( void );
void INTERRUPT_Initialize(void);
void CAN_Transmit(void);
void TMR1_Initialize (void);
void TMR1_Start( void );

volatile unsigned int ecan1MsgBuf[NUM_OF_ECAN_BUFFERS][8]
__attribute__((aligned(NUM_OF_ECAN_BUFFERS * 16)));



/*
 *
 */
int main( void ) {
    configOsc();
    configIO();
    configDMA();
    configCAN();
    INTERRUPT_Initialize();
    TMR1_Initialize();
    PR1 = 0xFFF;
    TMR1_Start();
   
    PORTCbits.RC4 = 0;
   
    while(1){
        CAN_Transmit();
    }
    return (EXIT_SUCCESS);
}

void TMR1_Initialize (void)
{
    //TMR1 0;
    TMR1 = 0x00;
    //Period = 0 s; Frequency = 16000000 Hz; PR1 0;
    PR1 = 0x00;
    //TCKPS 1:1; TON enabled; TSIDL disabled; TCS FOSC/2; TSYNC disabled; TGATE disabled;
    T1CON = 0x8000;

    IFS0bits.T1IF = 0;
    IEC0bits.T1IE = 1;

}

void TMR1_Start( void )
{
    /*Enable the interrupt*/
    IEC0bits.T1IE = 1;

    /* Start the Timer */
    T1CONbits.TON = 1;
}

void __attribute__ ( ( interrupt, no_auto_psv ) ) _T1Interrupt (  )
{
    /* Check if the Timer Interrupt/Status is set */

    //***User Area Begin
    PORTCbits.RC4 = ~PORTCbits.RC4;
    //***User Area End

    IFS0bits.T1IF = 0;
}

void configOsc(void)
{
    // FRCDIV FRC/2; PLLPRE 2; DOZE 1:8; PLLPOST 1:8; DOZEN disabled; ROI disabled;
    CLKDIV = 0x31C0;
    // TUN Center frequency;
    OSCTUN = 0x0;
    // ROON disabled; ROSEL disabled; RODIV Base clock value; ROSSLP disabled;
    REFOCON = 0x0;
    // PLLDIV 62;
    PLLFBD = 0x3E;
    // RND disabled; SATB disabled; SATA disabled; ACCSAT disabled;
CORCONbits.RND = 0;
CORCONbits.SATB = 0;
CORCONbits.SATA = 0;
CORCONbits.ACCSAT = 0;
    // CF no clock failure; NOSC PRIPLL; CLKLOCK unlocked; OSWEN Switch is Complete;
    __builtin_write_OSCCONH((uint8_t) ((0x300 >> _OSCCON_NOSC_POSITION) & 0x00FF));
    __builtin_write_OSCCONL((uint8_t) ((0x300 | _OSCCON_OSWEN_MASK) & 0xFF));
    // Wait for Clock switch to occur
    while (OSCCONbits.OSWEN != 0);
    while (OSCCONbits.LOCK != 1);
}

void configIO( void )
{
    /****************************************************************************
     * Setting the Output Latch SFR(s)
     ***************************************************************************/
    LATA = 0x0000;
    LATB = 0x0000;
    LATC = 0x0000;
    LATD = 0x0000;
    LATE = 0x0000;
    LATF = 0x0000;
    LATG = 0x0000;

    /****************************************************************************
     * Setting the GPIO Direction SFR(s)
     ***************************************************************************/
    TRISA = 0x1F93;
    TRISB = 0xFFFF;
    TRISC = 0x3F87;
    TRISD = 0x0160;
    TRISE = 0xF000;
    TRISF = 0x0001;     // CAN RX on RF0, CAN TX on RF1
    TRISG = 0x03C0;

    /****************************************************************************
     * Setting the Weak Pull Up and Weak Pull Down SFR(s)
     ***************************************************************************/
    // No pull up resistors are used for this program
    CNPDA = 0x0000;
    CNPDB = 0x0000;
    CNPDC = 0x0000;
    CNPDD = 0x0000;
    CNPDE = 0x0000;
    CNPDF = 0x0000;
    CNPDG = 0x0000;
    CNPUA = 0x0000;
    CNPUB = 0x0000;
    CNPUC = 0x0000;
    CNPUD = 0x0000;
    CNPUE = 0x0000;
    CNPUF = 0x0000;
    CNPUG = 0x0000;

    /****************************************************************************
     * Setting the Open Drain SFR(s)
     ***************************************************************************/
    ODCA = 0x0000;
    ODCB = 0x0000;
    ODCC = 0x0000;
    ODCD = 0x0000;
    ODCE = 0x0000;
    ODCF = 0x0000;
    ODCG = 0x0000;

    /****************************************************************************
     * Setting the Analog/Digital Configuration SFR(s)
     ***************************************************************************/
    ANSELA = 0x1E93;
    ANSELB = 0x038F;
    ANSELC = 0x0F87;
    ANSELE = 0xF000;
    ANSELG = 0x03C0;

    /****************************************************************************
     * Set the PPS
     ***************************************************************************/
    RPINR26bits.C1RXR = 0x0060;    //;   //RF0->ECAN1:C1RX
    RPOR9bits.RP97R = 0x000E;    //;   //RF1->ECAN1:C1TX
}

void configCAN( void )
{
    // Disable interrupts before the Initialization
    IEC2bits.C1IE = 0;
    C1INTE = 0;
   
    // put CAAN in configuration mode
    C1CTRL1bits.REQOP = 4;
    while (C1CTRL1bits.OPMODE != 4);
    /* clear window bit to access ECAN control registers */
    C1CTRL1bits.WIN = 0;
   
    /* Set up the baud rate*/
    C1CFG1 = 0x41; //BRP ;
    C1CFG2 = 0x1EE; //WAKFIL enabled; SEG2PHTS Freely programmable; SEG2PH 1 x TQ; SEG1PH 5 x TQ; PRSEG 6 x TQ; SAM Three times at the sample point;
    C1FCTRL = 0xC01F; //FSA Transmit/Receive Buffer TRB1; DMABS 32;
    C1FEN1 = 0x00; //FLTEN8 disabled; FLTEN7 disabled; FLTEN9 disabled; FLTEN0 disabled; FLTEN2 disabled; FLTEN10 disabled; FLTEN1 disabled; FLTEN11 disabled; FLTEN4 disabled; FLTEN3 disabled; FLTEN6 disabled; FLTEN5 disabled; FLTEN12 disabled; FLTEN13 disabled; FLTEN14 disabled; FLTEN15 disabled;
    C1CTRL1 = 0x800; //CANCKS FOSC; CSIDL disabled; ABAT disabled; REQOP Sets Normal Operation Mode; WIN Uses buffer window; CANCAP disabled;

    /* ECAN1, Buffer 0 is a Transmit Buffer */
    C1TR01CONbits.TXEN0 = 0x1; // Buffer 0 is a Transmit Buffer
    C1TR01CONbits.TXEN1 = 0x0; // Buffer 1 is a Receive Buffer
    C1TR23CONbits.TXEN2 = 0x0; // Buffer 2 is a Receive Buffer
    C1TR23CONbits.TXEN3 = 0x0; // Buffer 3 is a Receive Buffer
    C1TR45CONbits.TXEN4 = 0x0; // Buffer 4 is a Receive Buffer
    C1TR45CONbits.TXEN5 = 0x0; // Buffer 5 is a Receive Buffer
    C1TR67CONbits.TXEN6 = 0x0; // Buffer 6 is a Receive Buffer
    C1TR67CONbits.TXEN7 = 0x0; // Buffer 7 is a Receive Buffer

    C1TR01CONbits.TX0PRI = 0x0; // Message Buffer 0 Priority Level 0
    C1TR01CONbits.TX1PRI = 0x0; // Message Buffer 1 Priority Level 0
    C1TR23CONbits.TX2PRI = 0x0; // Message Buffer 2 Priority Level 0
    C1TR23CONbits.TX3PRI = 0x0; // Message Buffer 3 Priority Level 0
    C1TR45CONbits.TX4PRI = 0x0; // Message Buffer 4 Priority Level 0
    C1TR45CONbits.TX5PRI = 0x0; // Message Buffer 5 Priority Level 0
    C1TR67CONbits.TX6PRI = 0x0; // Message Buffer 6 Priority Level 0
    C1TR67CONbits.TX7PRI = 0x0; // Message Buffer 7 Priority Level 0
   
    // Place the ECAN module in Normal mode.
    C1CTRL1bits.REQOP = 0;
    while (C1CTRL1bits.OPMODE != 0);
   
    // Enable interrupts before the Initialization
    IEC2bits.C1IE = 1;
    C1INTE = 1;
}

void configDMA( void )
{
    // Initialize channels which are enabled   
    // Transmit channel
    DMA0CONbits.CHEN = 0;   //disable DMA0 for initialization
    // AMODE Peripheral Indirect Addressing mode; CHEN disabled; DIR Reads from RAM address, writes to peripheral address; HALF Initiates interrupt when all of the data has been moved; SIZE 16 bit; NULLW disabled; MODE Continuous, Ping-Pong modes are disabled;
    DMA0CON= 0x2020;// & 0x7FFF; //Enable DMA Channel later;
    // FORCE disabled; IRQSEL ECAN1 TX;
    DMA0REQ= 0x46;  //0x70 for kit example from 33EV_main_v11
    // STA 0;
    DMA0STAH= (unsigned int)&ecan1MsgBuf;   // from 33EV_main_v11
    // STA 4096;
    DMA0STAL= (unsigned int)&ecan1MsgBuf;   // from 33EV_main_v11
    // STB 0;
    DMA0STBH= 0x00;
    // STB 0;
    DMA0STBL= 0x00;
    // PAD 0;
    DMA0PAD= 0x0442;
    // CNT 7;
    DMA0CNT= 0x07;
    // Clearing Channel 0 Interrupt Flag;
    IFS0bits.DMA0IF = 0;
    // Enabling Channel 0 Interrupt
    IEC0bits.DMA0IE = 1;
   
    C1TR01CONbits.TXEN0 = 0x1;          // Buffer 0 is the Transmit Buffer
    C1TR01CONbits.TX0PRI = 0x0;         // transmit buffer priority
   
    // enable DMA0
    DMA0CONbits.CHEN = 1;
}

/**
    void INTERRUPT_Initialize (void)
*/
void INTERRUPT_Initialize (void)
{
    //    DMA1I: DMA Channel 1
    //    Priority: 1
        IPC3bits.DMA1IP = 1;
    //    DMA0I: DMA Channel 0
    //    Priority: 1
        IPC1bits.DMA0IP = 1;
    //    CI: ECAN1 Event
    //    Priority: 1
        IPC8bits.C1IP = 1;
    //    CTXI: CAN1 TX Data Request
    //    Priority: 1
        IPC17bits.C1TXIP = 1;
    //    TI: Timer 1
    //    Priority: 1
        IPC0bits.T1IP = 1;
}

void CAN_Transmit(void)
{
    unsigned int MSG_SID = 0x123;
    // write to the first transmit buffer
    // write to the first segment of the first transmit buffer
    ecan1MsgBuf[0][0] = MSG_SID << 2;

    ecan1MsgBuf[0][1] = 0x0000;
    /* CiTRBnDLC = 0b0000 0000 xxx0 1111
    EID<17:6> = 0b000000
    RTR = 0b0
    RB1 = 0b0
    RB0 = 0b0
    DLC = 6 */
    ecan1MsgBuf[0][2] = 0x0006;

    // Write message 6 data bytes:
    //
    ecan1MsgBuf[0][3] = 0x3333; // switch data, leading zeros
    ecan1MsgBuf[0][4] = 0x4444;
    ecan1MsgBuf[0][5] = 0x5555;

    Nop();
    Nop();
    Nop();
    /* Request message buffer 0 transmission */
    C1TR01CONbits.TXREQ0 = 0x1;
    /* The following shows an example of how the TXREQ bit can be polled to check if transmission
    is complete. */
    Nop();
    Nop();
    Nop();
    while (C1TR01CONbits.TXREQ0 == 1);
    // Message was placed successfully on the bus, return
}
« Last Edit: December 12, 2018, 11:30:59 pm by tech_builder »
 

Offline JPortici

  • Super Contributor
  • ***
  • Posts: 2560
  • Country: it
Re: CANbus with dsPIC33EV problem
« Reply #1 on: December 13, 2018, 06:41:00 am »
Three notes:
-ALWAYS check the board schematic. they are using the standby feature of the can transceiver.
-Re-read the device datasheet on the can peripheral. For it to work reliably you have to dedicate two DMA channels. One for receiving, one for transmitting. Choose your DMA channels carefully, the one which mustn't be interrupted first. I now use channel 2 and 3 for can, period. I had issues where results from the ADC were screwed up when using CAN because the ADC DMA would get interrupted by the CAN DMA :)
-didn't check your can timing registers but you might want to compare them against the calculators like the one integrated in mplab x (it's a plugin) or from the kvaser website (the peripheral comes from kvaser) o more generally this one http://www.bittiming.can-wiki.info/

anyway the thing you see looks like an error frame to me :) because it's not getting an ACK from the network, because the transceiver isn't active..
« Last Edit: December 13, 2018, 06:44:14 am by JPortici »
 

Offline AndyC_772

  • Super Contributor
  • ***
  • Posts: 3497
  • Country: gb
  • Professional design engineer
    • Cawte Engineering | Reliable Electronics
Re: CANbus with dsPIC33EV problem
« Reply #2 on: December 13, 2018, 07:44:42 am »
If there's a signal on the output of the PIC, but not on the CAN bus itself, then the transceiver must not be enabled.

Without the transceiver, the PIC won't see its own transmission on the Rx pin, and this will confuse the CAN hardware. CAN is designed for a noisy, multi-master environment, and the transmit logic includes checking that the signal is actually appearing correctly on the bus.

Check if there's a GPIO output from the PIC which is connected to the transceiver enable, and make sure it's active.

Offline tech_builder

  • Contributor
  • Posts: 40
  • Country: ca
Re: CANbus with dsPIC33EV problem
« Reply #3 on: December 20, 2018, 02:05:31 am »
Thanks so much for the help. I went back and started looking at my code again used the CAN calculator mentioned in JPortici's post and lo and behold I had been using Fcy to calculate my numbers instead of Fcan :-// ! So I made that change and I also set up the receive buffers and DMA channel, and it works!! Well sort of, I am now getting a stuff error after every successful transmission. It looks like it is starting another transmission right after the first one and then aborting after the start of frame is sent. Maybe something else is interrupting it? Anyways, below is a more documented version of my updated code.



For the Configuration bits I have it set to 16MHz crystal oscillator with PLL with clock switch disabled.
Code: [Select]
// DSPIC33EV256GM106 Configuration Bit Settings
 
// 'C' source line config statements
 
// FSEC
#pragma config BWRP = OFF            // Boot Segment Write-Protect Bit (Boot Segment may be written)
#pragma config BSS = DISABLED        // Boot Segment Code-Protect Level bits (No Protection (other than BWRP))
#pragma config BSS2 = OFF            // Boot Segment Control Bit (No Boot Segment)
#pragma config GWRP = OFF            // General Segment Write-Protect Bit (General Segment may be written)
#pragma config GSS = DISABLED        // General Segment Code-Protect Level bits (No Protection (other than GWRP))
#pragma config CWRP = OFF           // Configuration Segment Write-Protect Bit (Configuration Segment may be written)
#pragma config CSS = DISABLED        // Configuration Segment Code-Protect Level bits (No Protection (other than CWRP))
#pragma config AIVTDIS = DISABLE    // Alternate Interrupt Vector Table Disable Bit  (Disable Alternate Vector Table)
 
// FBSLIM
#pragma config BSLIM = 0x1FFF        // Boot Segment Code Flash Page Address Limit Bits (Enter Hexadecimal value)
 
// FOSCSEL
#pragma config FNOSC = PRIPLL        // Initial oscillator Source Selection Bits (Primary Oscillator with PLL module (XT + PLL, HS + PLL, EC + PLL))
#pragma config IESO = OFF            // Two Speed Oscillator Start-Up Bit (Start up device with FRC,then automatically switch to user selected oscillator source)
 
// FOSC
#pragma config POSCMD = XT          // Primary Oscillator Mode Select Bits (XT Crystal Oscillator mode)
#pragma config OSCIOFNC = ON        // OSC2 Pin I/O Function Enable Bit (OSC2 is general purpose digital I/O pin)
#pragma config IOL1WAY = OFF        // Peripheral Pin Select Configuration Bit (Allow Multiple reconfigurations)
#pragma config FCKSM = CSDCMD        // Clock Switching Mode Bits (Both Clock Switching and Fail-safe Clock Monitor are disabled)
#pragma config PLLKEN = ON          // PLL Lock Enable Bit (Clock switch to PLL source will wait until the PLL lock signal is valid)
 
// FWDT
#pragma config WDTPOST = PS32768    // Watchdog Timer Postscaler Bits (1:32,768)
#pragma config WDTPRE = PR128        // Watchdog Timer Prescaler Bit (1:128)
#pragma config FWDTEN = OFF          // Watchdog Timer Enable Bits (WDT and SWDTEN Disabled)
#pragma config WINDIS = OFF          // Watchdog Timer Window Enable Bit (Watchdog timer in Non-Window Mode)
#pragma config WDTWIN = WIN25        // Watchdog Window Select Bits (WDT Window is 25% of WDT period)
 
// FPOR
#pragma config BOREN0 = ON          // Brown Out Reset Detection Bit (BOR is Enabled)
 
// FICD
#pragma config ICS = PGD2            // ICD Communication Channel Select Bits (Communicate on PGEC2 and PGED2)
 
// FDMTINTVL
#pragma config DMTIVTL = 0xFFFF      // Lower 16 Bits of 32 Bit DMT Window Interval (Enter Hexadecimal value)
 
// FDMTINTVH
#pragma config DMTIVTH = 0xFFFF      // Upper 16 Bits of 32 Bit DMT Window Interval (Enter Hexadecimal value)
 
// FDMTCNTL
#pragma config DMTCNTL = 0xFFFF      // Lower 16 Bits of 32 Bit DMT Instruction Count Time-Out Value (Enter Hexadecimal value)
 
// FDMTCNTH
#pragma config DMTCNTH = 0xFFFF      // Upper 16 Bits of 32 Bit DMT Instruction Count Time-Out Value (Enter Hexadecimal value)
 
// FDMT
#pragma config DMTEN = DISABLE      // Dead Man Timer Enable Bit (Dead Man Timer is Disabled and can be enabled by software)
 
// FDEVOPT
#pragma config PWMLOCK = ON          // PWM Lock Enable Bit (Certain PWM registers may only be written after key sequence)
#pragma config ALTI2C1 = OFF        // Alternate I2C1 Pins Selection Bit (I2C1 mapped to SDA1/SCL1 pins)
 
// FALTREG
#pragma config CTXT1 = NONE          // Interrupt Priority Level (IPL) Selection Bits For Alternate Working Register Set 1 (Not Assigned)
#pragma config CTXT2 = NONE          // Interrupt Priority Level (IPL) Selection Bits For Alternate Working Register Set 2 (Not Assigned)
 
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.


This is the oscillator initialization routine. The development board comes with a 8MHz crystal oscillator. I have it set up for a Fcy of 16MHz. FOSCSEL -> FNOSC is set to 0b011 which is primary oscillator with PLL. FOSC -> POSCMD is set to 0b01, which is crystal oscillator mode.
Doze is not used CLKDIV -> DOZEN = 0. The CLKDIV -> PLLPOST = 0b11 is set to divide by 8. CLKDIV -> PLLPRE = 0b00000 is set to divide by 2.
The PLL feedback divisor is set to 64, which is PLLFBD -> PLLDIV = 0x3E.
Not sure if I need to write to OSCCON since the oscillator is setup in the #pragma statements at the beginning of the file.

Code: [Select]
void configOsc(void)
{
// FRCDIV FRC/2; PLLPRE 2; DOZE 1:8; PLLPOST 1:8; DOZEN disabled; ROI disabled;
CLKDIV = 0x31C0;
// TUN Center frequency;
OSCTUN = 0x0;
// ROON disabled; ROSEL disabled; RODIV Base clock value; ROSSLP disabled;
REFOCON = 0x0;
// PLLDIV 62;
PLLFBD = 0x3E;
// RND disabled; SATB disabled; SATA disabled; ACCSAT disabled;
            CORCONbits.RND = 0;
            CORCONbits.SATB = 0;
            CORCONbits.SATA = 0;
            CORCONbits.ACCSAT = 0;
// CF no clock failure; NOSC PRIPLL; CLKLOCK unlocked; OSWEN Switch is Complete;
    __builtin_write_OSCCONH((uint8_t) ((0x300 >> _OSCCON_NOSC_POSITION) & 0x00FF));
    __builtin_write_OSCCONL((uint8_t) ((0x300 | _OSCCON_OSWEN_MASK) & 0xFF));
// Wait for Clock switch to occur
while (OSCCONbits.OSWEN != 0);
while (OSCCONbits.LOCK != 1);
}


Here the pins are set to inputs/outputs. This was set using the MCC. Peripheral Pin Select is also set here to be the CAN receive RF0->ECAN1:C1RX and CAN transmit RF1->ECAN1:C1TX

Code: [Select]
void configIO( void )
{
    /****************************************************************************
  * Setting the Output Latch SFR(s)
  ***************************************************************************/
LATA = 0x0000;
LATB = 0x0000;
LATC = 0x0000;
LATD = 0x0000;
LATE = 0x0000;
LATF = 0x0000;
LATG = 0x0000;
 
    /****************************************************************************
  * Setting the GPIO Direction SFR(s)
     ***************************************************************************/
TRISA = 0x1F93;
TRISB = 0xFFFF;
TRISC = 0x3F87;
TRISD = 0x0160;
TRISE = 0xF000;
TRISF = 0x0001; // CAN RX on RF0, CAN TX on RF1
TRISG = 0x03C0;
 
    /****************************************************************************
  * Setting the Weak Pull Up and Weak Pull Down SFR(s)
     ***************************************************************************/
// No pull up resistors are used for this program
CNPDA = 0x0000;
CNPDB = 0x0000;
CNPDC = 0x0000;
CNPDD = 0x0000;
CNPDE = 0x0000;
CNPDF = 0x0000;
CNPDG = 0x0000;
CNPUA = 0x0000;
CNPUB = 0x0000;
CNPUC = 0x0000;
CNPUD = 0x0000;
CNPUE = 0x0000;
CNPUF = 0x0000;
CNPUG = 0x0000;
 
    /****************************************************************************
  * Setting the Open Drain SFR(s)
  ***************************************************************************/
ODCA = 0x0000;
ODCB = 0x0000;
ODCC = 0x0000;
ODCD = 0x0000;
ODCE = 0x0000;
ODCF = 0x0000;
ODCG = 0x0000;
 
    /****************************************************************************
  * Setting the Analog/Digital Configuration SFR(s)
     ***************************************************************************/
ANSELA = 0x1E93;
ANSELB = 0x038F;
ANSELC = 0x0F87;
ANSELE = 0xF000;
ANSELG = 0x03C0;
 
    /****************************************************************************
  * Set the PPS
     ***************************************************************************/
RPINR26bits.C1RXR = 0x0060; //;   //RF0->ECAN1:C1RX
RPOR9bits.RP97R = 0x000E; //;   //RF1->ECAN1:C1TX
}

The CANbus is configured here. Fbaud is set to 250kbps and the time quanta in a bit time is set to 16TQ ie 16 TQ/bit. C1CTRL1 -> CANCKS = 1 which implies Fcan = 2*Fcy which is 2*16MHz = 32MHz. SEG2PHTS is set to be freely programmable = 1; SEG2PH 1 x TQ; SEG1PH 7 x TQ; PRSEG 4 x TQ; SAM is set to sample three times at the sample point =1.
The first 8 buffers are configured with the first buffer set as a transmit buffer and the rest as receive buffers. The priority of all the buffers is set to the lowest setting = 0.
Code: [Select]
void configCAN( void )
{
// Disable interrupts before the Initialization
IEC2bits.C1IE = 0;
C1INTE = 0;

// put CAN in configuration mode
C1CTRL1bits.REQOP = 4;
while (C1CTRL1bits.OPMODE != 4);
/* clear window bit to access ECAN control registers */
C1CTRL1bits.WIN = 0;

    /* Set up the baud rate*/
    //C1CFG1 = 0x0003; //BRP=3 ie CANCKS=1 => Fcan = 2*Fcy = 32MHz ;
    C1CFG1bits.BRP = 0b11;  // BRP value is 3
    C1CFG1bits.SJW = 0b00;  // SJW synchronization jump width is set to 1xTQ
    //C1CFG2 = 0x01FC; //WAKFIL disabled; SEG2PHTS Freely programmable; SEG2PH 1 x TQ; SEG1PH 7 x TQ; PRSEG 4 x TQ; SAM Three times at the sample point;
    C1CFG2bits.WAKFIL = 0;
    C1CFG2bits.SEG2PH = 0b01;
    C1CFG2bits.SEG2PHTS = 0b1;
    C1CFG2bits.SAM = 0b1;       // sample 3 times
    C1CFG2bits.SEG1PH = 0b111;
    C1CFG2bits.PRSEG = 0b100;
    //C1FCTRL = 0xC01F; //FSA Transmit/Receive Buffer TRB1; DMABS 32;
    C1FCTRLbits.DMABS = 0b110;  // 32 buffers in device RAM
    C1FCTRLbits.FSA = 0b011111;
    C1FEN1 = 0x00; //FLTEN8 disabled; FLTEN7 disabled; FLTEN9 disabled; FLTEN0 disabled; FLTEN2 disabled; FLTEN10 disabled; FLTEN1 disabled; FLTEN11 disabled; FLTEN4 disabled; FLTEN3 disabled; FLTEN6 disabled; FLTEN5 disabled; FLTEN12 disabled; FLTEN13 disabled; FLTEN14 disabled; FLTEN15 disabled;
    //C1CTRL1 = 0x800; //CANCKS FOSC; CSIDL disabled; ABAT disabled; REQOP Sets Normal Operation Mode; WIN Uses buffer window; CANCAP disabled;
    C1CTRL1bits.CSIDL = 0;  // continue module operation in idle mode
    C1CTRL1bits.ABAT = 0;   // Module will clear this bit when all transmissions are aborted
    C1CTRL1bits.CANCKS = 1; // Fcan = 2*Fcy => Fcan = 2*16MHz = 32MHz
    C1CTRL1bits.CANCAP = 0; //disable CAN capture   
 
/* ECAN1, Buffer 0 is a Transmit Buffer */
    C1TR01CONbits.TXEN0 = 0x1; // Buffer 0 is a Transmit Buffer
C1TR01CONbits.TXEN1 = 0x0; // Buffer 1 is a Receive Buffer
    C1TR23CONbits.TXEN2 = 0x0; // Buffer 2 is a Receive Buffer
    C1TR23CONbits.TXEN3 = 0x0; // Buffer 3 is a Receive Buffer
    C1TR45CONbits.TXEN4 = 0x0; // Buffer 4 is a Receive Buffer
C1TR45CONbits.TXEN5 = 0x0; // Buffer 5 is a Receive Buffer
    C1TR67CONbits.TXEN6 = 0x0; // Buffer 6 is a Receive Buffer
    C1TR67CONbits.TXEN7 = 0x0; // Buffer 7 is a Receive Buffer
 
    C1TR01CONbits.TX0PRI = 0x0; // Message Buffer 0 Priority Level 0
C1TR01CONbits.TX1PRI = 0x0; // Message Buffer 1 Priority Level 0
    C1TR23CONbits.TX2PRI = 0x0; // Message Buffer 2 Priority Level 0
    C1TR23CONbits.TX3PRI = 0x0; // Message Buffer 3 Priority Level 0
    C1TR45CONbits.TX4PRI = 0x0; // Message Buffer 4 Priority Level 0
    C1TR45CONbits.TX5PRI = 0x0; // Message Buffer 5 Priority Level 0
    C1TR67CONbits.TX6PRI = 0x0; // Message Buffer 6 Priority Level 0
    C1TR67CONbits.TX7PRI = 0x0; // Message Buffer 7 Priority Level 0

// Place the ECAN module in Normal mode.
C1CTRL1bits.REQOP = 0;
while (C1CTRL1bits.OPMODE != 0);

// Enable interrupts before the Initialization
IEC2bits.C1IE = 1;
C1INTE = 1;
}


This is where the DMA initialization for use with the CAN peripheral is done. DMA0CON is set up as the transmit channel with word size transfer size, peripheral indirect addressing mode and continuous with ping pong disabled mode. DMA3CON is configured as the receive channel with similar setting to DMA0.

Code: [Select]
void configDMA( void )
{
    // Initialize channels which are enabled   
    // Transmit channel
    DMA0CONbits.CHEN = 0;   //disable DMA0 for initialization
    // AMODE Peripheral Indirect Addressing mode; CHEN disabled; DIR Reads from RAM address, writes to peripheral address; HALF Initiates interrupt when all of the data has been moved; SIZE 16 bit; NULLW disabled; MODE Continuous, Ping-Pong modes are disabled;
    // DMA0CON= 0x2020;// & 0x7FFF; //Enable DMA Channel later;
    DMA0CONbits.SIZE = 0;   // word size data transfer
    DMA0CONbits.DIR = 1;    // read from DPSRAM address, write to peripheral address
    DMA0CONbits.NULLW = 0;  // normal operation
    DMA0CONbits.AMODE = 0b10;   // peripheral indirect addressing mode
    DMA0CONbits.MODE = 0b00;    // continuous, ping pong modes disabled
    // FORCE disabled; IRQSEL ECAN1 TX;
    // DMA0REQ = 0x46;//0x46;  //0x70 for kit example from 33EV_main_v11
    DMA0REQbits.FORCE = 0;      // automatic DMA transfer initiation by DMA request
    DMA0REQbits.IRQSEL = 0x46;  // DMA peripheral IRQ number -> CAN Tx IRQ is 0x46
    // STA 0;
    DMA0STAH= 0x00;//(unsigned int)&ecan1MsgBuf;   // from 33EV_main_v11
    // STA 4096;
    DMA0STAL= (unsigned int)&ecan1MsgBuf;   // from 33EV_main_v11
    // STB 0;
    DMA0STBH= 0x00;
    // STB 0;
    DMA0STBL= 0x00;
    // PAD 0;
    DMA0PAD= 0x0442;    // Peripheral to DMA association
    // CNT 7;
    DMA0CNT= 0x07;
    // Clearing Channel 0 Interrupt Flag;
    IFS0bits.DMA0IF = 0;
    // Enabling Channel 0 Interrupt
    IEC0bits.DMA0IE = 1;
   
    // Receive channel DMA3
    DMA3CONbits.CHEN = 0;   //disable DMA0 for initialization
   
    DMA3CONbits.SIZE = 0;   // word size data transfer
    DMA3CONbits.DIR = 0;    // read from peripheral address, write to DPSRAM address
    DMA3CONbits.NULLW = 0;  // normal operation
    DMA3CONbits.AMODE = 0b10;   // peripheral indirect addressing mode
    DMA3CONbits.MODE = 0b00;    // continuous, ping pong modes disabled

    DMA3REQbits.FORCE = 0;      // automatic DMA transfer initiation by DMA request
    DMA3REQbits.IRQSEL = 0x22;  // DMA peripheral IRQ number -> CAN Rx IRQ is 0x22
    // STA 0;
    DMA3STAH= 0x00;//(unsigned int)&ecan1MsgBuf;   // from 33EV_main_v11
    // STA 4096;
    DMA3STAL= (unsigned int)&ecan1MsgBuf;   // from 33EV_main_v11
    // STB 0;
    DMA3STBH= 0x00;
    // STB 0;
    DMA3STBL= 0x00;
    // PAD 0;
    DMA3PAD= 0x0440;    // Peripheral to DMA association
    // CNT 7;
    DMA3CNT= 0x07;
    // Clearing Channel 0 Interrupt Flag (table pg 66 device datasheet);
    IFS2bits.DMA3IF = 0;
    // Enabling Channel 0 Interrupt
    IEC2bits.DMA3IE = 1;
   
    // enable DMA0
    DMA0CONbits.CHEN = 1;
    // enable DMA3
    DMA3CONbits.CHEN = 1;
}


The IPCxbits set the priority for each of the peripheral interrupts. They have all been set to 1.
Code: [Select]
/**
void INTERRUPT_Initialize (void)
*/
void INTERRUPT_Initialize (void)
{
// The priority of each interrupt for each peripheral is set below
// DMA1I: DMA Channel 1
// Priority: 1
        IPC3bits.DMA1IP = 1;
// DMA0I: DMA Channel 0
// Priority: 1
        IPC1bits.DMA0IP = 1;
// CI: ECAN1 Event
// Priority: 1
    IPC8bits.C1IP = 1;
// CTXI: CAN1 TX Data Request
// Priority: 1
        IPC17bits.C1TXIP = 1;
// TI: Timer 1
// Priority: 1
    IPC0bits.T1IP = 1;
}


This is the CAN transmit function and was copied from the demo code that came with the development board.
Code: [Select]
void CAN_Transmit(void)
{
unsigned int MSG_SID = 0x123;
// write to the first transmit buffer
// write to the first segment of the first transmit buffer
ecan1MsgBuf[0][0] = MSG_SID << 2;
 
ecan1MsgBuf[0][1] = 0x0000;
/* CiTRBnDLC = 0b0000 0000 xxx0 1111
EID<17:6> = 0b000000
RTR = 0b0
RB1 = 0b0
RB0 = 0b0
DLC = 6 */
ecan1MsgBuf[0][2] = 0x0006;
 
// Write message 6 data bytes:
//
ecan1MsgBuf[0][3] = 0x3333; // switch data, leading zeros
ecan1MsgBuf[0][4] = 0x4444;
ecan1MsgBuf[0][5] = 0x5555;
 
Nop();
Nop();
Nop();
/* Request message buffer 0 transmission */
    C1TR01CONbits.TXREQ0 = 0x1;
/* The following shows an example of how the TXREQ bit can be polled to check if transmission
is complete. */
Nop();
Nop();
Nop();
    while (C1TR01CONbits.TXREQ0 == 1);
// Message was placed successfully on the bus, return
}

There it is. Hopefully this can get somebody else started with CAN on the dev board :)
« Last Edit: December 20, 2018, 03:07:10 am by tech_builder »
 

Offline JPortici

  • Super Contributor
  • ***
  • Posts: 2560
  • Country: it
Re: CANbus with dsPIC33EV problem
« Reply #4 on: December 20, 2018, 08:57:38 am »
Thanks so much for the help. I went back and started looking at my code again used the CAN calculator mentioned in JPortici's post and lo and behold I had been using Fcy to calculate my numbers instead of Fcan :-// ! So I made that change and I also set up the receive buffers and DMA channel, and it works!! Well sort of, I am now getting a stuff error after every successful transmission. It looks like it is starting another transmission right after the first one and then aborting after the start of frame is sent. Maybe something else is interrupting it?

could be, could be not :)

https://www.kvaser.com/about-can/the-can-protocol/can-error-handling/ (go to Bit Stuffing)

a stuff condition is seen, doesn't mean it's an error

I happen to know the guy that wrote the code for that demo board (he once was a microchip FAE, he now builds killer synthesizers! http://www.synthtech.com/ )
it's failry easy to understand, but i decided to throw all the CAN part away and write my own, one bit at a time until i had my library.
It is just a suggestion but divide your problem in three parts
1) the application code, with its buffers and data, with interfaces that generate can frames and retreive data from single frames
2) the can protocol code, which has its buffers for can RX and TX and handles stuff like networking (ISO15765-2 and -3 for example). here i defined my can_t frame (ID, flags, data) and can_buffer_t
3) the device specific code, so the interrupts, configuration and the interfaces that get a can_t frame and stuff the registers or the hardware buffers in the format required by the MCU and vice-versa. This is the part that you will have to change when porting code.

Case in point, i have designed a couple of OBD gadgets i ported between three architectures in which
1) Has the error codes list, status variables, IO over can handling etc. It also has the state machine that runs in the superloop so it doesn't hang in there too much
2) Is the code that get CAN frames and put them in a separate frame types which i called uds_t and obd_t which have a DTC list, size, index for read/write etc. Also handles the merging and splitting of multi-frame messages
3) Is the only part of code i had to change, no issues at all.

the three architectures were dsPIC33EV, PIC32MX and ATSAMC21. Luckily for me the 33EV and the 32MX both use the same CAN IP from kvaser, but with minor differences in other registers and how DMA plays a role in the peripheral (mandatory in the dsPIC and eats up two channels, dedicated in the PIC32) whereas the ATSAM used a completely different peripheral. Easy peasy.
 

Offline tech_builder

  • Contributor
  • Posts: 40
  • Country: ca
Re: CANbus with dsPIC33EV problem
« Reply #5 on: December 24, 2018, 01:39:43 am »
Thanks again. I will need to work with the J1939 protocol, so this is just the beginning of the journey :). I am glad I finally have something working! This is a very complicated peripheral and to mix it in with the DMA controller adds another level of complexity to the confusion. I will be looking into your suggestions for program structure when I get there. Thanks again!
 

Offline PIC18F2550

  • Newbie
  • Posts: 2
  • Country: dz
Re: CANbus with dsPIC33EV problem
« Reply #6 on: September 14, 2019, 10:35:52 am »
hello friend tech_builder

if you can send me the firmware i tree to help you , the DMA system is for controlle automaticly the address ram buffers[][] in the dsPIC33EV only

the DMA is option ,can you don't use this option
http://ww1.microchip.com/downloads/en/devicedoc/70182c.pdf
« Last Edit: September 15, 2019, 01:13:50 pm by PIC18F2550 »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf