view main/packages/avr/can/m1.cpp @ 220:74381e5dd204 main

Demo for the Adafruit LCD product number 1480.
author Bob Cook <bob@bobcookdev.com>
date Mon, 07 Jul 2014 18:42:15 -0700
parents b903d0499d23
children
line wrap: on
line source

// ----------------------------------------------------------------------------------------
//
//  avr/can/m1.cpp
//    
//  Bob Cook Development, Robotics Library
//  http://www.bobcookdev.com/rl/
//    
//  This file implements CAN support for the mega(16,32,64)m1 chip.
//
//  Copyright (C) 2012-2014 Bob Cook
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//
// ----------------------------------------------------------------------------------------

#include "m1.h"

#include <avr/io.h>

#include "packages/avr/device/int_helpers.h"
#include "packages/avr/device/spi.h"
#include "packages/avr/device/spinwait.h"

#include "packages/common/util/cbuffer.h"

// ----------------------------------------------------------------------------------------

#include "project_defs.h"

#if defined( PRJ_M1CAN_ENABLE )

#include "packages/avr/can/m1_guard_can_int_disable.h"

#endif

#if defined( PRJ_M1CAN_ENABLE_DEBUG )

#include <stdio.h>
#include <avr/pgmspace.h>

#endif

// ----------------------------------------------------------------------------------------

#if defined( PRJ_M1CAN_ENABLE )

#if !defined( PRJ_M1CAN_RX_BUFFER_SIZE )
#define PRJ_M1CAN_RX_BUFFER_SIZE 4
#endif

#if !defined( PRJ_M1CAN_TX_BUFFER_SIZE )
#define PRJ_M1CAN_TX_BUFFER_SIZE 4
#endif

// ----------------------------------------------------------------------------------------

typedef struct
{
    uint32_t id;
    uint8_t  length;
    uint8_t  data[ 8 ];

}   m1can_data_t;

static cbuffer< m1can_data_t, uint8_t, PRJ_M1CAN_RX_BUFFER_SIZE > g_rx_buffer;
static cbuffer< m1can_data_t, uint8_t, PRJ_M1CAN_TX_BUFFER_SIZE > g_tx_buffer;

volatile uint8_t g_status;

// ----------------------------------------------------------------------------------------
//  mapping the four register bytes to the uint32_t CAN identifier; this assumes the
//  correct CANPAGE has been selected previously.

static uint32_t get_can_id()
{
    uint32_t result;
     
    result  = static_cast< uint32_t >( CANIDT4 ) >>  3;
    result |= static_cast< uint32_t >( CANIDT3 ) <<  5;
    result |= static_cast< uint32_t >( CANIDT2 ) << 13;
    result |= static_cast< uint32_t >( CANIDT1 ) << 21;

    return result;
}

// ----------------------------------------------------------------------------------------
//  write the four register bytes from the uint32_t CAN identifier; this assumes the
//  correct CANPAGE has been selected previously.

static void set_can_id( uint32_t id )
{
    CANIDT4 = static_cast< uint8_t >( id <<  3 );
    CANIDT3 = static_cast< uint8_t >( id >>  5 );
    CANIDT2 = static_cast< uint8_t >( id >> 13 );
    CANIDT1 = static_cast< uint8_t >( id >> 21 );
}

// ----------------------------------------------------------------------------------------
//  Assumes the correct CANPAGE has been selected previously.

static void read_into_rx_buffer()    
{
    m1can_data_t  msg;

    //--    Copy out the message identifier.

    msg.id = get_can_id();

    //--    Retrieve the request flag or data.

    if ( CANIDT4 & ( 1 << RTRTAG ) )
    {
        msg.length = 0xff;  // signifies this is a request rather than a message
    }
    else
    {
        msg.length = CANCDMOB & 0x0f;

        for ( uint8_t i = 0; i < msg.length; ++i )
        {
            msg.data[ i ] = CANMSG;
        }
    }

    //--    Add it to the queue, if there is space. Otherwise discard the overflow.

    if ( g_tx_buffer.is_full() )
    {
        g_status |= m1can_status_overflow;
    }
    else
    {
        g_rx_buffer.push( msg );
    }
}

// ----------------------------------------------------------------------------------------
//  Load the transmit buffer (CANPAGE 0) with the given data.

static void send_data( const uint32_t& can_id, const uint8_t* data_ptr, uint8_t length )
{
    //--    Select the correct CANPAGE for TX.

    CANPAGE = 0;

    //--    Load the destination address.
    
    set_can_id( can_id );

    //--    Load the data.

    for ( uint8_t i = 0; i < length; ++i )
    {
        CANMSG = *data_ptr++;
    }

    //--    Trigger the TX for this Mob.
    
    CANCDMOB = ( 0 << CONMOB1 ) | ( 1 << CONMOB0 ) | ( 1 << IDE ) | length;
}

// ----------------------------------------------------------------------------------------
//  Load the transmit buffer (CANPAGE 0) with the given request.

static void send_request( const uint32_t& can_id )
{
    //--    Select the correct CANPAGE for TX.

    CANPAGE = 0;

    //--    Load the destination address.
    
    set_can_id( can_id );

    //--    Tag this as a request.

    CANIDT4 |= ( 1 << RTRTAG );

    //--    Trigger the TX for this Mob.
    
    CANCDMOB = ( 0 << CONMOB1 ) | ( 1 << CONMOB0 ) | ( 1 << IDE );
}

// ----------------------------------------------------------------------------------------
//  Load the transmit buffer from the TX queue.

static void send_from_tx_buffer()
{
    m1can_data_t  msg = g_tx_buffer.pop();

    if ( msg.length == 0xff )
    {
        send_request( msg.id );
    }
    else
    {
        send_data( msg.id, msg.data, msg.length );
    }
}

// ----------------------------------------------------------------------------------------

bool m1can_init()
{
    //--    Reset the CAN controller.

    if ( ! m1can_reset() )
    {
        return false;
    }

    //--    Set up the bus speed via the bit timing registers; this depends on the input
    //      clock frequency for the chip.

#if ( PRJ_CPU_FREQ == 16000000 )

#if defined( PRJ_M1CAN_CANBUS_125_KHZ )

    CANBT1 = ( 0 << BRP5 ) | ( 0 << BRP4 ) | ( 0 << BRP3 )      // BRP = 7 + 1
           | ( 1 << BRP2 ) | ( 1 << BRP1 ) | ( 1 << BRP0 )
           ;

    CANBT2 = ( 0 << SJW1 ) | ( 0 << SJW0 )                      // SWJ = 0
           | ( 1 << PRS2 ) | ( 1 << PRS1 ) | ( 0 << PRS0 )      // PRS = 6 + 1
           ;

    CANBT3 = ( 0 << PHS22 ) | ( 1 << PHS21 ) | ( 1 << PHS20 )   // PHSEG2 = 3 + 1
           | ( 0 << PHS12 ) | ( 1 << PHS11 ) | ( 1 << PHS10 )   // PHSEG1 = 3 + 1
           | ( 1 << SMP )                                       // SMP = 1
           ;

#elif defined( PRJ_M1CAN_CANBUS_250_KHZ )

    CANBT1 = ( 0 << BRP5 ) | ( 0 << BRP4 ) | ( 0 << BRP3 )      // BRP = 3 + 1
           | ( 0 << BRP2 ) | ( 1 << BRP1 ) | ( 1 << BRP0 )
           ;

    CANBT2 = ( 0 << SJW1 ) | ( 0 << SJW0 )                      // SWJ = 0 + 1
           | ( 1 << PRS2 ) | ( 1 << PRS1 ) | ( 0 << PRS0 )      // PRS = 6 + 1
           ;

    CANBT3 = ( 0 << PHS22 ) | ( 1 << PHS21 ) | ( 1 << PHS20 )   // PHSEG2 = 3 + 1
           | ( 0 << PHS12 ) | ( 1 << PHS11 ) | ( 1 << PHS10 )   // PHSEG1 = 3 + 1
           | ( 1 << SMP )                                       // SMP = 1
           ;

#elif defined( PRJ_M1CAN_CANBUS_500_KHZ )

    CANBT1 = ( 0 << BRP5 ) | ( 0 << BRP4 ) | ( 0 << BRP3 )      // BRP = 1 + 1
           | ( 0 << BRP2 ) | ( 0 << BRP1 ) | ( 1 << BRP0 )
           ;

    CANBT2 = ( 0 << SJW1 ) | ( 0 << SJW0 )                      // SWJ = 0 + 1
           | ( 1 << PRS2 ) | ( 1 << PRS1 ) | ( 0 << PRS0 )      // PRS = 6 + 1
           ;

    CANBT3 = ( 0 << PHS22 ) | ( 1 << PHS21 ) | ( 1 << PHS20 )   // PHSEG2 = 3 + 1
           | ( 0 << PHS12 ) | ( 1 << PHS11 ) | ( 1 << PHS10 )   // PHSEG1 = 3 + 1
           | ( 1 << SMP )                                       // SMP = 1
           ;

#else
#error Unsupported CAN bus speed selection!
#endif

#else   // #if ( PRJ_CPU_FREQ == 16000000 )
#error Unsupported CPU speed selection!
#endif

    //--    Enable Mob 0 for TX, Mob 1 through 5 for RX.

    CANPAGE  = 0;
    CANSTMOB = 0;
    CANCDMOB = 0;

    for ( uint8_t i = 1; i <= 5; ++i )
    {
        CANPAGE  = ( i << 4 );
        CANSTMOB = 0;
        CANIDM1  = 0;
        CANIDM2  = 0;
        CANIDM3  = 0;
        CANIDM4  = 0;
        CANCDMOB = ( 1 << CONMOB1 ) | ( 0 << CONMOB0 ) | ( 1 << IDE );
    }

    CANEN2 = ( 1 << ENMOB5 ) | ( 1 << ENMOB4 ) | ( 1 << ENMOB3 )
           | ( 1 << ENMOB2 ) | ( 1 << ENMOB1 ) | ( 1 << ENMOB0 )
           ;

    CANIE2 = ( 1 << IEMOB5 ) | ( 1 << IEMOB4 ) | ( 1 << IEMOB3 ) 
           | ( 1 << IEMOB2 ) | ( 1 << IEMOB1 ) | ( 1 << IEMOB0 )
           ;

    CANHPMOB = 0;

    //--    Enable the RX and TX interrupts.

    CANGIE = ( 1 << ENIT ) | ( 1 << ENRX ) | ( 1 << ENTX );

    //--    Enable the CAN controller.

    CANGCON = ( 1 << ENASTB );

    return true;
}

// ----------------------------------------------------------------------------------------
    
bool m1can_reset()
{
    CANGCON |= ( 1 << SWRES );

    for ( uint16_t i = 0; i < 50000; ++i )
    {
        if ( ( CANGCON & ( 1 << SWRES ) ) == 0 )
        {
            return true;
        }
    }
    
    return false; // failed to get out of the reset state
}

// ----------------------------------------------------------------------------------------

ISR( CAN_INT_vect )
{
    //--    Valid interrupt for a TX or RX?

    if ( CANSIT2 == 0 )
    {
        return;
    }

    //--    Get the highest priority Mob, then set CANPAGE to that Mob.
    
    CANPAGE = CANHPMOB;

    //--    Process either the TX or RX interrupt.

    if ( CANSTMOB & ( 1 << TXOK ) )
    {
        CANSTMOB &= ~( 1 << TXOK ); // clear interrupt reason

        if ( ! g_tx_buffer.is_empty() )
        {
            spinwait_delay_ms( 1 );
            send_from_tx_buffer();
        }
        else
        {
            CANCDMOB &= ~( ( 1 << CONMOB1 ) | ( 1 << CONMOB0 ) );
        }
    }
    
    if ( CANSTMOB & ( 1 << RXOK ) )
    {
        read_into_rx_buffer();
        CANSTMOB &= ~( 1 << RXOK ); // clear interrupt reason
        CANCDMOB = ( 1 << CONMOB1 ) | ( 0 << CONMOB0 ) | ( 1 << IDE ); // enable rx
    }
}

// ----------------------------------------------------------------------------------------

bool m1can_read( uint32_t* can_id, bool* is_request, uint8_t* data_ptr, uint8_t* length )
{
    //--    Pending message?

    if ( g_rx_buffer.is_empty() )
    {
        return false;
    }

    //--    Fetch it from the queue and copy it out.

    m1can_data_t msg = g_rx_buffer.pop();

    *can_id = msg.id;
    
    if ( msg.length == 0xff )
    {
        *is_request = true;
    }
    else
    {
        *is_request = false;
        *length     = msg.length;

        for ( uint8_t i = 0; i < msg.length; i++ )
        {
            *data_ptr++ = msg.data[ i ];
        }
    }

    return true;
}

// ----------------------------------------------------------------------------------------

bool m1can_send( const uint32_t& can_id, const uint8_t* data_ptr, uint8_t length )
{
    //--    Disable the CAN interrupt to avoid specific race conditions.

    m1_guard_can_int_disable can_int_disabled;    

    //--    We might be able to send this message immediately, if the TX buffer is empty
    //      and there is not a transmit already in progress.

    if ( g_tx_buffer.is_empty() )
    {
        CANPAGE = 0;

        if ( ( CANCDMOB & ( ( 1 << CONMOB1 ) | ( 1 << CONMOB0 ) ) ) == 0 )
        {
            send_data( can_id, data_ptr, length );
            return true;
        }
    }

    //--    Deferred transmit. Got space in the buffer?

    if ( g_tx_buffer.is_full() )
    {
        return false;
    }

    //--    Add the message to the buffer, then enable the interrupt to trigger a
    //      transmit.

    m1can_data_t  msg;
    
    msg.id     = can_id;
    msg.length = length;

    for ( uint8_t i = 0; i < length; i++ )
    {
        msg.data[ i ] = *data_ptr++;
    }
    
    g_tx_buffer.push( msg );

    return true;
}

// ----------------------------------------------------------------------------------------
    
bool m1can_request( const uint32_t& can_id )
{
    //--    Disable the CAN interrupt to avoid specific race conditions.

    m1_guard_can_int_disable can_int_disabled;    

    //--    We might be able to send this request immediately, if the TX buffer is empty
    //      and there is not a transmit already in progress.

    if ( g_tx_buffer.is_empty() )
    {
        CANPAGE = 0;

        if ( ( CANCDMOB & ( ( 1 << CONMOB1 ) | ( 1 << CONMOB0 ) ) ) == 0 )
        {
            send_request( can_id );
            return true;
        }
    }

    //--    Deferred transmit. Got space in the buffer?

    if ( g_tx_buffer.is_full() )
    {
        return false;
    }

    //--    Add the message to the buffer, then enable the interrupt to trigger a
    //      transmit.

    m1can_data_t msg;
    
    msg.id     = can_id;
    msg.length = 0xff; // request, not an actual message

    g_tx_buffer.push( msg );

    return true;
}

// ----------------------------------------------------------------------------------------

uint8_t m1can_status()
{
    uint8_t result = g_status;
    g_status = m1can_status_ok;
    return result;
}

// ----------------------------------------------------------------------------------------

#if defined( PRJ_M1CAN_ENABLE_DEBUG )
    
static void print_register( const prog_char* label, uint8_t value )
{
    printf_P( label );
    
    for ( uint8_t i = 0; i < 8; i++ )
    {
        if ( value & 0x80 )
        {
            putchar( '1' );
        }
        else
        {
            putchar( '0' );
        }
        value = value << 1;
    }

    putchar( '\n' );
}

void m1can_debug_print_registers()
{
    printf_P( PSTR("mega(16,32,64)m1 CAN registers:\n") );

    print_register( PSTR("    CANGCON : "), CANGCON );
    print_register( PSTR("    CANGSTA : "), CANGSTA );
    print_register( PSTR("    CANGIT  : "), CANGIT );
    print_register( PSTR("    CANGIE  : "), CANGIE );
    putchar( '\n' );

    print_register( PSTR("    CANEN1  : "), CANEN1 );
    print_register( PSTR("    CANEN2  : "), CANEN2 );
    putchar( '\n' );
    
    print_register( PSTR("    CANBT1  : "), CANBT1 );
    print_register( PSTR("    CANBT2  : "), CANBT2 );
    print_register( PSTR("    CANBT3  : "), CANBT3 );
    putchar( '\n' );

    for ( uint8_t i = 0; i <= 5; ++i )
    {
        CANPAGE &= 0x0f;
        CANPAGE |= ( i << 4 );

        print_register( PSTR("    %1d CANPAGE  : "), i, CANPAGE );
        print_register( PSTR("        CANSTMOB : "), CANSTMOB );
        print_register( PSTR("        CANCDMOB : "), CANCDMOB );
        print_register( PSTR("        CANIDT1  : "), CANIDT1 );
        print_register( PSTR("        CANIDT2  : "), CANIDT2 );
        print_register( PSTR("        CANIDT3  : "), CANIDT3 );
        print_register( PSTR("        CANIDT4  : "), CANIDT4 );
        putchar( '\n' );
    }
}

#endif  // PRJ_M1CAN_ENABLE_DEBUG

// ----------------------------------------------------------------------------------------

#endif  // PRJ_M1CAN_ENABLE

// ----------------------------------------------------------------------------------------