Implementação de Comunicação CAN Bus para TMS320F28335

Este artigo apresenta uma implementação completa de comunicação via CAN Bus para o processador TMS320F28335, incluindo inicialização do módulo CAN, configuração de múltiplos buffers de comunicação, gerenciamento de interrupções para transmissão e receção, tratamento de erros e suporte ao protocolo CANopen.

Características do Módulo eCAN TMS320F28335


Característica Descrição
Número de buffers 32 buffers (16 TX, 16 RX)
Taxa de transmissão Até 1Mbps
Protocolo CAN 2.0B (quadros padrão e estendidos)
Tipo de buffer Configurável como TX ou RX
Interrupções Interrupções de receção/transmissão/erro
Filtragem Registros de máscara de receção

Configuração recomendada:

  • Taxa de transmissão: 500kbps
  • Número de buffers: 8 para transmissão, 8 para receção
  • Modo de interrupção: Interrupção por buffer de receção
  • Clock: 30MHz (clock do sistema 60MHz, com divisão)

Conexão de Hardware


Pinagem do DSP28335 para CAN:
GPIO30 → CANRXA
GPIO31 → CANTXA

Circuito externo:
DSP CAN → Transceptor CAN(TJA1050) → Barramento CAN
       ↓       ↓
     3.3V    Alimentação 5V


Importante: É obrigatório o uso de transceptor, pois o CAN do opera em nível TTL e necessita conversão para sinal diferencial.

Arquitetura do Programa


Biblioteca_CAN/
├── can_driver.c        # Driver principal do CAN
├── can_driver.h        # Arquivo de cabeçalho
├── can_config.c       # Funções de configuração
├── can_isr.c          # Rotinas de interrupção
└── can_app.c          # Exemplo de implementação


Cabeçalho do Driver CAN


// can_driver.h
#ifndef CAN_DRIVER_H
#define CAN_DRIVER_H

#include "DSP2833x_Device.h"
#include "DSP2833x_Examples.h"

// Número de buffers do CAN
#define CAN_BUFFER_COUNT    32
#define CAN_TX_BUFFER_START 16
#define CAN_RX_BUFFER_START 0

// Estados do CAN
typedef enum {
    CAN_STATE_UNINIT = 0,
    CAN_STATE_READY,
    CAN_STATE_BUS_OFF,
    CAN_STATE_ERROR
} CAN_State_t;

// Direção do buffer
typedef enum {
    CAN_BUFFER_DIR_RX = 0,
    CAN_BUFFER_DIR_TX
} CAN_BufferDir_t;

// Tipo de quadro
typedef enum {
    CAN_FRAME_STD = 0,  // Quadro padrão
    CAN_FRAME_EXT       // Quadro estendido
} CAN_FrameType_t;

// Configuração do buffer de receção
typedef struct {
    Uint16 buffer_num;      // Número do buffer 0-31
    Uint32 message_id;      // ID da mensagem
    CAN_FrameType_t frame_type;  // Tipo de quadro
    Uint16 mask;           // Máscara de aceitação
    Uint16 data_len;       // Comprimento dos dados
} CAN_RxBufferConfig_t;

// Configuração do buffer de transmissão
typedef struct {
    Uint16 buffer_num;      // Número do buffer 0-31
    Uint32 message_id;      // ID da mensagem
    CAN_FrameType_t frame_type;  // Tipo de quadro
    Uint16 data_len;       // Comprimento dos dados
    Uint16 priority;       // Prioridade de transmissão
} CAN_TxBufferConfig_t;

// Estrutura de mensagem recebida
typedef struct {
    Uint32 message_id;      // ID da mensagem
    CAN_FrameType_t frame_type;  // Tipo de quadro
    Uint16 data_len;       // Comprimento dos dados
    Uint8 data[8];        // Dados
    Uint16 timestamp;      // Carimbo de tempo
    Uint16 buffer_num;    // Número do buffer
} CAN_RxMessage_t;

// Estrutura de mensagem para transmissão
typedef struct {
    Uint32 message_id;      // ID da mensagem
    CAN_FrameType_t frame_type;  // Tipo de quadro
    Uint16 data_len;       // Comprimento dos dados
    Uint8 data[8];        // Dados
    Uint16 buffer_num;    // Número do buffer
} CAN_TxMessage_t;

// Contadores de erro
typedef struct {
    Uint16 rx_error_count;
    Uint16 tx_error_count;
    Uint16 bus_off_count;
    Uint16 error_passive_count;
} CAN_ErrorCount_t;

// Handle do CAN
typedef struct {
    CAN_State_t state;
    CAN_ErrorCount_t error_count;
    Uint16 baud_rate;      // Taxa de transmissão em kbps
    Uint16 interrupt_mask; // Máscara de interrupções
    void (*rx_callback)(CAN_RxMessage_t* msg);  // Callback de receção
    void (*error_callback)(Uint16 error_code);  // Callback de erro
} CAN_Handle_t;

// Declaração de funções
void CAN_Init(Uint16 baud_rate);
void CAN_ConfigBuffer(CAN_BufferDir_t dir, Uint16 buffer_num, 
                     Uint32 msg_id, CAN_FrameType_t frame_type);
Uint16 CAN_SendData(Uint16 buffer_num, Uint8* data, Uint16 len);
Uint16 CAN_ReceiveData(Uint16 buffer_num, CAN_RxMessage_t* msg);
void CAN_SetRxCallback(void (*callback)(CAN_RxMessage_t*));
void CAN_EnableInterrupt(Uint16 buffer_num, Uint16 enable);
void CAN_ErrorHandler(void);
Uint16 CAN_GetStatus(void);
void CAN_BusOffRecovery(void);

// Funções de configuração de taxa
void CAN_SetBaudRate(Uint16 baud_rate);

// Rotina de serviço de interrupção
interrupt void CAN_ISR(void);

#endif

Inicialização e Configuração do CAN


// can_config.c
#include "can_driver.h"

// Handle global do CAN
CAN_Handle_t can_handle = {0};

// Função de inicialização do CAN
void CAN_Init(Uint16 baud_rate)
{
    // 1. Habilitar clock do eCAN
    EALLOW;
    SysCtrlRegs.PCLKCR0.bit.ECANAENCLK = 1;  // Habilitar clock do eCAN-A
    EDIS;
    
    // 2. Configurar GPIO para função CAN
    EALLOW;
    GpioCtrlRegs.GPAMUX2.bit.GPIO30 = 3;  // CANRXA
    GpioCtrlRegs.GPAMUX2.bit.GPIO31 = 3;  // CANTXA
    EDIS;
    
    // 3. Inicializar registradores de controle do CAN
    // Entrar no modo de inicialização
    ECanaRegs.CANMC.bit.CCR = 1;  // Solicitar mudança de configuração
    while(ECanaRegs.CANES.bit.CCE != 1) {}  // Aguardar habilitação de configuração
    
    // 4. Configurar parâmetros de temporização
    CAN_SetBaudRate(baud_rate);
    
    // 5. Configurar registradores de máscara de receção
    // Máscara de receção global
    ECanaMboxes.MBOX0.MSGID.all = 0x00000000;  // Sem máscara
    
    // 6. Sair do modo de inicialização
    ECanaRegs.CANMC.bit.CCR = 0;
    while(ECanaRegs.CANES.bit.CCE != 0) {}  // Aguardar desabilitação de configuração
    
    // 7. Inicializar buffers
    CAN_InitBuffers();
    
    // 8. Inicializar interrupções
    CAN_InitInterrupts();
    
    can_handle.state = CAN_STATE_READY;
    can_handle.baud_rate = baud_rate;
}

// Configuração da taxa de transmissão
void CAN_SetBaudRate(Uint16 baud_rate)
{
    Uint16 brp, tseg1, tseg2, sjw;
    
    // Clock do sistema = 150MHz (supondo SYSCLKOUT=150MHz)
    // Clock do CAN = SYSCLKOUT / (BRP + 1)
    // Taxa de transmissão = Clock do CAN / ((TSEG1 + TSEG2 + 1) * (BRP + 1))
    
    switch(baud_rate) {
        case 1000:  // 1Mbps
            brp = 14;   // 150/(15)=10MHz
            tseg1 = 5;  // 5+1=6
            tseg2 = 2;  // 2+1=3
            sjw = 1;
            break;
            
        case 500:   // 500kbps
            brp = 29;   // 150/(30)=5MHz
            tseg1 = 5;  // 5+1=6
            tseg2 = 2;  // 2+1=3
            sjw = 1;
            break;
            
        case 250:   // 250kbps
            brp = 59;   // 150/(60)=2.5MHz
            tseg1 = 5;  // 5+1=6
            tseg2 = 2;  // 2+1=3
            sjw = 1;
            break;
            
        case 125:   // 125kbps
            brp = 119;  // 150/(120)=1.25MHz
            tseg1 = 5;  // 5+1=6
            tseg2 = 2;  // 2+1=3
            sjw = 1;
            break;
            
        default:    // 500kbps
            brp = 29;
            tseg1 = 5;
            tseg2 = 2;
            sjw = 1;
    }
    
    // Configurar registrador de temporização
    ECanaRegs.CANBTC.bit.BRP = brp;
    ECanaRegs.CANBTC.bit.TSEG1 = tseg1;
    ECanaRegs.CANBTC.bit.TSEG2 = tseg2;
    ECanaRegs.CANBTC.bit.SJW = sjw;
}

// Inicialização dos buffers
void CAN_InitBuffers(void)
{
    Uint16 i;
    
    // 1. Inicializar todos os buffers
    for(i = 0; i < CAN_BUFFER_COUNT; i++) {
        // Limpar registrador de controle do buffer
        ECanaMboxes.MBOX[i].MSGCTRL.all = 0x00000000;
        
        // Limpar ID da mensagem
        ECanaMboxes.MBOX[i].MSGID.all = 0x00000000;
        
        // Limpar dados
        ECanaMboxes.MBOX[i].MDL.all = 0x00000000;
        ECanaMboxes.MBOX[i].MDH.all = 0x00000000;
        
        // Configurar como buffer de receção por padrão
        if(i < CAN_TX_BUFFER_START) {
            // Buffer de receção
            ECanaRegs.CANME.all &= ~(1 << i);  // Desabilitar buffer
            ECanaRegs.CANMD.all &= ~(1 << i);  // Configurar como receção
        } else {
            // Buffer de transmissão
            ECanaRegs.CANME.all &= ~(1 << i);  // Desabilitar buffer
            ECanaRegs.CANMD.all |= (1 << i);   // Configurar como transmissão
        }
    }
    
    // 2. Configurar registrador de direção (CANMD)
    // Buffers 0-15: Buffers de receção
    // Buffers 16-31: Buffers de transmissão
    ECanaRegs.CANMD.all = 0xFFFF0000;
    
    // 3. Habilitar buffers
    ECanaRegs.CANME.all = 0x00000000;  // Desabilitar todos os buffers inicialmente
    
    // 4. Configurar máscara de receção
    // Usar registradores de máscara 0-2
    ECanaRegs.CANLAM0.all = 0xFFFFFFFF;  // Receção global
    ECanaRegs.CANLAM1.all = 0xFFFFFFFF;
    ECanaRegs.CANLAM2.all = 0xFFFFFFFF;
}

// Configuração de buffer individual
void CAN_ConfigBuffer(CAN_BufferDir_t dir, Uint16 buffer_num, 
                     Uint32 msg_id, CAN_FrameType_t frame_type)
{
    if(buffer_num >= CAN_BUFFER_COUNT) return;
    
    // 1. Desabilitar buffer
    ECanaRegs.CANME.all &= ~(1 << buffer_num);
    
    // 2. Definir direção do buffer
    if(dir == CAN_BUFFER_DIR_RX) {
        ECanaRegs.CANMD.all &= ~(1 << buffer_num);  // Receção
    } else {
        ECanaRegs.CANMD.all |= (1 << buffer_num);   // Transmissão
    }
    
    // 3. Configurar ID da mensagem
    if(frame_type == CAN_FRAME_STD) {
        // Quadro padrão
        ECanaMboxes.MBOX[buffer_num].MSGID.all = msg_id & 0x7FF;  // 11 bits de ID
        ECanaMboxes.MBOX[buffer_num].MSGID.bit.IDE = 0;  // Quadro padrão
    } else {
        // Quadro estendido
        ECanaMboxes.MBOX[buffer_num].MSGID.all = msg_id & 0x1FFFFFFF;  // 29 bits de ID
        ECanaMboxes.MBOX[buffer_num].MSGID.bit.IDE = 1;  // Quadro estendido
    }
    
    // 4. Habilitar buffer
    ECanaRegs.CANME.all |= (1 << buffer_num);
}

// Configuração do callback de receção
void CAN_SetRxCallback(void (*callback)(CAN_RxMessage_t*))
{
    can_handle.rx_callback = callback;
}

Funções de Transmissão e Receção do CAN


// can_driver.c
#include "can_driver.h"

// Função de envio de mensagem
Uint16 CAN_SendData(Uint16 buffer_num, Uint8* data, Uint16 len)
{
    Uint32* pData = (Uint32*)data;
    
    if(buffer_num < CAN_TX_BUFFER_START || buffer_num >= CAN_BUFFER_COUNT) {
        return 0;  // Número de buffer inválido
    }
    
    if(len > 8) len = 8;  // CAN suporta no máximo 8 bytes
    
    // 1. Aguardar buffer pronto
    if(ECanaRegs.CANTRS.all & (1 << buffer_num)) {
        // Buffer ocupado, aguardar liberação
        while(ECanaRegs.CANTRS.all & (1 << buffer_num)) {}
    }
    
    // 2. Definir comprimento dos dados
    ECanaMboxes.MBOX[buffer_num].MSGCTRL.bit.DLC = len;
    
    // 3. Escrever dados
    if(len > 0) {
        ECanaMboxes.MBOX[buffer_num].MDL.all = pData[0];
    }
    if(len > 4) {
        ECanaMboxes.MBOX[buffer_num].MDH.all = pData[1];
    }
    
    // 4. Disparar transmissão
    ECanaRegs.CANTRS.all = (1 << buffer_num);
    
    // 5. Aguardar confirmação de transmissão
    Uint32 timeout = 10000;  // Contador de timeout
    while((ECanaRegs.CANTA.all & (1 << buffer_num)) == 0) {
        timeout--;
        if(timeout == 0) {
            can_handle.error_count.tx_error_count++;
            return 0;  // Timeout na transmissão
        }
    }
    
    // 6. Limpar flag de confirmação
    ECanaRegs.CANTA.all = (1 << buffer_num);
    
    return len;
}

// Função de receção (modo polling)
Uint16 CAN_ReceiveData(Uint16 buffer_num, CAN_RxMessage_t* msg)
{
    if(buffer_num >= CAN_TX_BUFFER_START) {
        return 0;  // Não é buffer de receção
    }
    
    // 1. Verificar se há nova mensagem
    if((ECanaRegs.CANRMP.all & (1 << buffer_num)) == 0) {
        return 0;  // Sem nova mensagem
    }
    
    // 2. Ler ID da mensagem
    if(ECanaMboxes.MBOX[buffer_num].MSGID.bit.IDE) {
        // Quadro estendido
        msg->frame_type = CAN_FRAME_EXT;
        msg->message_id = ECanaMboxes.MBOX[buffer_num].MSGID.all & 0x1FFFFFFF;
    } else {
        // Quadro padrão
        msg->frame_type = CAN_FRAME_STD;
        msg->message_id = ECanaMboxes.MBOX[buffer_num].MSGID.bit.STDMSGID;
    }
    
    // 3. Ler comprimento dos dados
    msg->data_len = ECanaMboxes.MBOX[buffer_num].MSGCTRL.bit.DLC;
    
    // 4. Ler dados
    Uint32* pData = (Uint32*)msg->data;
    pData[0] = ECanaMboxes.MBOX[buffer_num].MDL.all;
    pData[1] = ECanaMboxes.MBOX[buffer_num].MDH.all;
    
    // 5. Limpar flag de receção
    ECanaRegs.CANRMP.all = (1 << buffer_num);
    
    // 6. Registrar número do buffer e timestamp
    msg->buffer_num = buffer_num;
    msg->timestamp = ECanaRegs.CANTSC.all;  // Contador de timestamp
    
    return msg->data_len;
}

// Envio de quadro estendido
Uint16 CAN_SendExtFrame(Uint32 id, Uint8* data, Uint16 len, Uint16 buffer_num)
{
    if(buffer_num < 16) buffer_num = 16;  // Usar buffer de transmissão
    
    // Configurar como quadro estendido
    ECanaMboxes.MBOX[buffer_num].MSGID.all = id & 0x1FFFFFFF;
    ECanaMboxes.MBOX[buffer_num].MSGID.bit.IDE = 1;  // Quadro estendido
    ECanaMboxes.MBOX[buffer_num].MSGID.bit.AME = 0;  // Sem máscara de receção
    
    return CAN_SendData(buffer_num, data, len);
}

// Envio de quadro padrão
Uint16 CAN_SendStdFrame(Uint16 id, Uint8* data, Uint16 len, Uint16 buffer_num)
{
    if(buffer_num < 16) buffer_num = 16;  // Usar buffer de transmissão
    
    // Configurar como quadro padrão
    ECanaMboxes.MBOX[buffer_num].MSGID.bit.STDMSGID = id & 0x7FF;
    ECanaMboxes.MBOX[buffer_num].MSGID.bit.IDE = 0;  // Quadro padrão
    ECanaMboxes.MBOX[buffer_num].MSGID.bit.AME = 0;  // Sem máscara de receção
    
    return CAN_SendData(buffer_num, data, len);
}

Configuração de Interrupções do CAN


// can_isr.c
#include "can_driver.h"

// Tabela de vetores de interrupção
#define CAN_ISR_VECTOR  INT14  // Vetor de interrupção CAN-A

// Rotina de serviço de interrupção
interrupt void CAN_ISR(void)
{
    Uint16 int_flag;
    CAN_RxMessage_t rx_msg;
    
    // 1. Ler flags de interrupção
    int_flag = ECanaRegs.CANGIF0.all;
    
    // 2. Interrupção de buffer
    if(int_flag & 0x00000001) {  // Interrupção de buffer 0-15
        // Verificar qual buffer gerou a interrupção
        for(int i = 0; i < 16; i++) {
            if(ECanaRegs.CANRMP.all & (1 << i)) {
                // Ler mensagem
                CAN_ReceiveData(i, &rx_msg);
                
                // Chamar callback
                if(can_handle.rx_callback != NULL) {
                    can_handle.rx_callback(&rx_msg);
                }
                
                // Limpar flag de interrupção
                ECanaRegs.CANRMP.all = (1 << i);
            }
        }
    }
    
    // 3. Interrupção de transmissão concluída
    if(int_flag & 0x00000002) {  // Interrupção de transmissão
        // Limpar flag de confirmação
        ECanaRegs.CANTA.all = ECanaRegs.CANTA.all;
    }
    
    // 4. Interrupção de erro
    if(int_flag & 0x00000004) {  // Interrupção de erro
        CAN_ErrorHandler();
    }
    
    // 5. Interrupção de wake-up
    if(int_flag & 0x00000008) {  // Interrupção de wake-up
        // Wake-up do barramento
        ECanaRegs.CANGIF0.bit.WUIF = 0;  // Limpar flag
    }
    
    // 6. Interrupção de bus-off
    if(int_flag & 0x00000010) {  // Bus-off
        can_handle.state = CAN_STATE_BUS_OFF;
        can_handle.error_count.bus_off_count++;
        
        if(can_handle.error_callback != NULL) {
            can_handle.error_callback(0x10);
        }
        
        ECanaRegs.CANGIF0.bit.BOIF = 0;  // Limpar flag
    }
    
    // 7. Interrupção de erro passivo
    if(int_flag & 0x00000020) {  // Erro passivo
        can_handle.error_count.error_passive_count++;
        
        if(can_handle.error_callback != NULL) {
            can_handle.error_callback(0x20);
        }
        
        ECanaRegs.CANGIF0.bit.EPIF = 0;  // Limpar flag
    }
    
    // Limpar flags globais de interrupção
    ECanaRegs.CANGIF0.all = int_flag;
    
    // Confirmar interrupção PIE
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP9;
}

// Inicialização das interrupções do CAN
void CAN_InitInterrupts(void)
{
    // 1. Habilitar interrupções PIE
    EALLOW;
    PieVectTable.CANINTA = &CAN_ISR;  // Interrupção CAN-A
    EDIS;
    
    // 2. Habilitar interrupções de buffer
    // Interrupções de buffer 0-15
    ECanaRegs.CANMIM.all = 0x0000FFFF;  // Habilitar interrupções de buffer 0-15
    
    // 3. Habilitar interrupções globais
    ECanaRegs.CANGIM.all = 0x0000001F;  // Habilitar todas as interrupções
    
    // 4. Configurar PIE
    PieCtrlRegs.PIEIER9.bit.INTx1 = 1;  // Habilitar INT9.1 (CAN-A)
    
    // 5. Habilitar INT9 do CPU
    IER |= M_INT9;
    
    // 6. Habilitar interrupções globais
    EINT;
    ERTM;
}

// Habilitar/desabilitar interrupções de buffer
void CAN_EnableInterrupt(Uint16 buffer_num, Uint16 enable)
{
    if(buffer_num >= CAN_BUFFER_COUNT) return;
    
    if(enable) {
        ECanaRegs.CANMIM.all |= (1 << buffer_num);
    } else {
        ECanaRegs.CANMIM.all &= ~(1 << buffer_num);
    }
}

Tratamento de Erros


// can_error.c
#include "can_driver.h"

// Função de tratamento de erros
void CAN_ErrorHandler(void)
{
    Uint16 error_status = ECanaRegs.CANES.all;
    
    // 1. Verificar status de erro
    if(error_status & 0x0001) {  // Erro de receção
        can_handle.error_count.rx_error_count++;
    }
    
    if(error_status & 0x0002) {  // Erro de transmissão
        can_handle.error_count.tx_error_count++;
    }
    
    if(error_status & 0x0004) {  // Erro passivo
        can_handle.state = CAN_STATE_ERROR;
    }
    
    if(error_status & 0x0008) {  // Bus-off
        can_handle.state = CAN_STATE_BUS_OFF;
        CAN_BusOffRecovery();
    }
    
    // 2. Chamar callback de erro
    if(can_handle.error_callback != NULL) {
        can_handle.error_callback(error_status);
    }
}

// Recuperação de bus-off
void CAN_BusOffRecovery(void)
{
    // 1. Entrar no modo de inicialização
    ECanaRegs.CANMC.bit.CCR = 1;
    while(ECanaRegs.CANES.bit.CCE != 1) {}
    
    // 2. Limpar contadores de erro
    ECanaRegs.CANTEC = 0;  // Contador de erro de transmissão
    ECanaRegs.CANREC = 0;  // Contador de erro de receção
    
    // 3. Reconfigurar temporização
    CAN_SetBaudRate(can_handle.baud_rate);
    
    // 4. Sair do modo de inicialização
    ECanaRegs.CANMC.bit.CCR = 0;
    while(ECanaRegs.CANES.bit.CCE != 0) {}
    
    // 5. Reabilitar buffers
    ECanaRegs.CANME.all = 0xFFFFFFFF;
    
    can_handle.state = CAN_STATE_READY;
}

// Obter status do CAN
Uint16 CAN_GetStatus(void)
{
    Uint16 status = 0;
    
    // Combinar informações de status
    status = (can_handle.state << 8) |
             (can_handle.error_count.rx_error_count & 0xFF);
    
    return status;
}

Implementação do Protocolo CANopen (Ocpional)


// canopen.c
#include "can_driver.h"

// Dicionário de objetos CANopen
typedef struct {
    Uint16 index;
    Uint8 subindex;
    Uint32 value;
} CANopen_OD_Entry_t;

// Funções do protocolo CANopen
Uint16 CANopen_SendSDO(Uint16 node_id, Uint16 index, Uint8 subindex, 
                      Uint8* data, Uint16 len, Uint8 expedited)
{
    Uint8 sdo_data[8];
    Uint32 sdo_cmd;
    
    // Construir comando SDO
    sdo_cmd = 0x600 + node_id;  // Envio SDO do cliente
    
    sdo_data[0] = expedited ? 0x23 : 0x22;  // Byte de comando
    sdo_data[1] = index & 0xFF;
    sdo_data[2] = (index >> 8) & 0xFF;
    sdo_data[3] = subindex;
    
    // Copiar dados
    for(int i = 0; i < len && i < 4; i++) {
        sdo_data[4 + i] = data[i];
    }
    
    return CAN_SendExtFrame(sdo_cmd, sdo_data, 8, 16);
}

Uint16 CANopen_SendPDO(Uint16 node_id, Uint8 pdo_num, Uint8* data, Uint16 len)
{
    Uint32 pdo_cmd;
    
    switch(pdo_num) {
        case 1:
            pdo_cmd = 0x200 + node_id;
            break;
        case 2:
            pdo_cmd = 0x300 + node_id;
            break;
        case 3:
            pdo_cmd = 0x400 + node_id;
            break;
        case 4:
            pdo_cmd = 0x500 + node_id;
            break;
        default:
            pdo_cmd = 0x200 + node_id;
    }
    
    return CAN_SendExtFrame(pdo_cmd, data, len, 16);
}

void CANopen_SendNMT(Uint8 node_id, Uint8 command)
{
    Uint8 nmt_data[2];
    
    nmt_data[0] = command;
    nmt_data[1] = node_id;
    
    CAN_SendExtFrame(0x000, nmt_data, 2, 16);
}

Exemplo de Programa Principal


// main.c
#include "DSP2833x_Device.h"
#include "can_driver.h"

// Variáveis globais
CAN_RxMessage_t rx_buffer[10];
Uint16 rx_index = 0;

// Função de callback de receção
void CAN_RxCallback(CAN_RxMessage_t* msg)
{
    if(rx_index < 10) {
        rx_buffer[rx_index] = *msg;
        rx_index++;
    }
}

// Função de callback de erro
void CAN_ErrorCallback(Uint16 error_code)
{
    // Registrar log de erro
    // Pode ser salvo em Flash ou reportado por outra interface
}

int main(void)
{
    // 1. Inicializar sistema
    InitSysCtrl();
    InitPieCtrl();
    InitPieVectTable();
    
    // 2. Inicializar CAN, 500kbps
    CAN_Init(500);
    
    // 3. Configurar buffers
    // Buffer 0: Receber quadro padrão, ID=0x100
    CAN_ConfigBuffer(CAN_BUFFER_DIR_RX, 0, 0x100, CAN_FRAME_STD);
    
    // Buffer 1: Receber quadro estendido, ID=0x12345678
    CAN_ConfigBuffer(CAN_BUFFER_DIR_RX, 1, 0x12345678, CAN_FRAME_EXT);
    
    // Buffer 16: Buffer de transmissão
    CAN_ConfigBuffer(CAN_BUFFER_DIR_TX, 16, 0x200, CAN_FRAME_STD);
    
    // 4. Configurar funções de callback
    CAN_SetRxCallback(CAN_RxCallback);
    
    // 5. Loop principal
    while(1) {
        // Enviar dados de teste
        Uint8 tx_data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
        
        CAN_SendStdFrame(0x200, tx_data, 8, 16);
        
        // Processar mensagens recebidas
        for(Uint16 i = 0; i < rx_index; i++) {
            // Processar mensagem recebida
            ProcessCANMessage(&rx_buffer[i]);
        }
        rx_index = 0;
        
        DELAY_US(1000000);  // Atraso de 1 segundo
    }
}

// Processar mensagem CAN
void ProcessCANMessage(CAN_RxMessage_t* msg)
{
    switch(msg->message_id) {
        case 0x100:
            // Processar mensagem com ID=0x100
            break;
            
        case 0x12345678:
            // Processar quadro estendido
            break;
    }
}

Técnicas de Depuração


1. Uso de analisador lógico

// Adicionar pinos de depuração
#define DEBUG_PIN_SET()   GpioDataRegs.GPASET.bit.GPIO0 = 1
#define DEBUG_PIN_CLR()   GpioDataRegs.GPACLEAR.bit.GPIO0 = 1

// Adicionar em pontos-chave
DEBUG_PIN_SET();
CAN_SendData(16, data, 8);
DEBUG_PIN_CLR();


2. Monitoramento de estado de erro

void CAN_MonitorTask(void)
{
    static Uint32 last_check = 0;
    Uint32 current_time = GetTimerCount();
    
    if(current_time - last_check > 1000) {  // Verificar a cada 1 segundo
        Uint16 status = CAN_GetStatus();
        
        if(status & 0xFF00) {  // Verificar bits de status
            printf("Status CAN anormal: 0x%04X\n", status);
        }
        
        last_check = current_time;
    }
}

Problemas Comuns


Problema Causa Solução
Sem comunicação Taxa de transmissão incompatível Verificar taxa em ambos os lados
Sem receção de dados Configuração de buffer incorreta Verificar direção e ID do buffer
Falha no envio Buffer ocupado Aguardar conclusão ou usar outro buffer
Erro no barramento Resistência de terminação ausente Adicionar resistor de 120Ω nas extremidades do barramento

Configuração de Compilação


1. Configuração do projeto CCS

Caminhos de Inclusão:
- C:\ti\c2000\C28x_FPU_LIB
- ${PROJECT_ROOT}/include

Símbolos Pré-definidos:
_DEBUG
CPU1


2. Arquivo de comando do linker

MEMORY
{
    PAGE 0:   /* Memória de Programa */
    PAGE 1:   /* Memória de Dados */
}


Tags: TMS320F28335 CAN Bus DSP Comunicação Serial Embedded Systems

Publicado em 6-20 01:09