Multiple twim masters on the AT32UC3C (solved)

Go To Last Post
15 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The ASF twim.c code seems to only support a single twi master since it uses a global twim_inst variable for the interrupt routine. I'm assuming there are no hardware limitations for running all three twi ports simultaneously as master, correct?

Last Edited: Thu. Jan 26, 2012 - 09:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are not hardware limitations. The driver is hardcoded to use the TWIM0 module. You just need to modify it to work with multiple TWI ports. I've done it and it works as it should.

Daniel Campora http://www.wipy.io

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Please file a bug report so we can take a look at it and schedule it for enhancement.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

abcminiuser wrote:
Please file a bug report so we can take a look at it and schedule it for enhancement.

- Dean :twisted:

I sent a ticket in to avr32@atmel.com - is that sufficient or is there an official bug reporting mechanism?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Darn, I thought the link text in my post above would be easy to spot.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mjordan wrote:
abcminiuser wrote:
Please file a bug report so we can take a look at it and schedule it for enhancement.

- Dean :twisted:

I sent a ticket in to avr32@atmel.com - is that sufficient or is there an official bug reporting mechanism?

Totally did NOT see it. The light blue is too light :)

Bug submitted: http://asf.atmel.com/bugzilla/show_bug.cgi?id=1693

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry if i post here, but i haven't found any other post for TWIM modules for UC3C chip. I would like to know why there is no function in the asf that can use the TWI without having some blocking code.

I am using those chips in UAV and we cannot have that kind of while "wait for device to finish".

Is there a way to modify the existing code to permit this easily ?

Guillaume Charland

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was toying around with this on my spare time - attached is a preliminary example and not fully tested AVR32 TWIM driver that supports asynchronous callbacks.

Use it as an example only since I can't guarantee anything about it -- if it works for you great, but I'd rather you use it as a reference for your own fully tested driver.

#include "twim.h"

#if UC3A3
#       if !defined(AVR32_TWIM0_GROUP)
#               define AVR32_TWIM0_GROUP         11
#       endif
#       if !defined(AVR32_TWIM1_GROUP)
#               define AVR32_TWIM1_GROUP         12
#       endif
#endif

#if UC3C
#       if !defined(AVR32_TWIM0_GROUP)
#               define AVR32_TWIM0_GROUP         25
#       endif
#       if !defined(AVR32_TWIM1_GROUP)
#               define AVR32_TWIM1_GROUP         26
#       endif
#       if !defined(AVR32_TWIM2_GROUP)
#               define AVR32_TWIM2_GROUP         45
#       endif
#endif

#if UC3D
#       if !defined(AVR32_TWIM_GROUP)
#                       define AVR32_TWIM_GROUP         10
#       endif
#endif

#if UC3L
#       if !defined(AVR32_TWIM0_GROUP)
#               define AVR32_TWIM0_GROUP         20
#       endif
#       if !defined(AVR32_TWIM1_GROUP)
#               define AVR32_TWIM1_GROUP         21
#       endif
#endif

/**
 * \internal
 *
 * \brief TWI master mode software instance struct.
 */
struct twi_master_instance {
        /*! Asynchronous callback function */
        twi_master_callback_t callback;
        
        /*! Indicates if the transfer should be a read or a write. */
        bool read;

        /*! Current logical status of the bus. */
        volatile status_code_t status;

        /*! Current package being transferred on the bus. */
        const twi_package_t *current_transfer;

        /*! Number of internal address bytes sent to the slave on the bus. */
        uint8_t sent_address_bytes;

        /*! Number of transferred bytes transferred from/to the slave on the bus. */
        uint16_t transferred_length;
};

/**
 * \internal
 *
 * \brief TWI master Interrupt Vectors
 *
 * The TWI master interrupt request entry points are conditionally compiled
 * for the TWI interfaces supported by the XMEGA MCU variant. All of these
 * entry points call a common service function, twi_master_interrupt_handler(),
 * to handle bus events.
 */
static void twi_master_interrupt_handler(volatile avr32_twim_t *const twi,
                struct twi_master_instance *instance);

#ifdef AVR32_TWIM0
struct twi_master_instance twim0_instance;

ISR(twim_master_interrupt_handler_twim0, AVR32_TWIM0_GROUP, CONF_TWIM_IRQ_LEVEL)
{
        twi_master_interrupt_handler(&AVR32_TWIM0, &twim0_instance);
}
#endif

#ifdef AVR32_TWIM1
struct twi_master_instance twim1_instance;

ISR(twim_master_interrupt_handler_twim1, AVR32_TWIM1_GROUP, CONF_TWIM_IRQ_LEVEL)
{
        twi_master_interrupt_handler(&AVR32_TWIM1, &twim1_instance);
}
#endif

#ifdef AVR32_TWIM2
struct twi_master_instance twim2_instance;

ISR(twim_master_interrupt_handler_twim2, AVR32_TWIM2_GROUP, CONF_TWIM_IRQ_LEVEL)
{
        twi_master_interrupt_handler(&AVR32_TWIM2, &twim2_instance);
}
#endif

/**
 * \internal
 *
 * \brief TWI master peripheral to instance conversion routine
 *
 * This function converts a base pointer to a TWI peripheral module into its
 * corresponding instance struct, which contains information about the module's
 * state.
 *
 * \return Corresponding instance struct for the given TWI module, or NULL if
 *         an invalid pointer is supplied
 */
static struct twi_master_instance *twi_master_instance_from_module(
                volatile avr32_twim_t *const twi)
{
        if (twi == NULL) {
                Assert(0);
        }
#ifdef AVR32_TWIM0
        else if ((uintptr_t)twi == (uintptr_t)&AVR32_TWIM0) {
                return &twim0_instance;
        }
#endif
#ifdef AVR32_TWIM1
        else if ((uintptr_t)twi == (uintptr_t)&AVR32_TWIM1) {
                return &twim1_instance;
        }
#endif
#ifdef AVR32_TWIM2
        else if ((uintptr_t)twi == (uintptr_t)&AVR32_TWIM2) {
                return &twim2_instance;
        }
#endif
        else {
                Assert(0);
        }

        return NULL;
}

static inline void twi_master_register_int_handlers(void)
{
#ifdef AVR32_TWIM0
        irq_register_handler(twim_master_interrupt_handler_twim0,
                        AVR32_TWIM0_IRQ, CONF_TWIM_IRQ_LEVEL);
#endif
#ifdef AVR32_TWIM1
        irq_register_handler(twim_master_interrupt_handler_twim1,
                        AVR32_TWIM1_IRQ, CONF_TWIM_IRQ_LEVEL);
#endif
#ifdef AVR32_TWIM2
        irq_register_handler(twim_master_interrupt_handler_twim2,
                        AVR32_TWIM2_IRQ, CONF_TWIM_IRQ_LEVEL);
#endif
}

/**
 * \internal
 *
 * \brief TWI master common interrupt handler.
 *
 *  This is the common master mode interrupt handler that manages TWI modules
 *  when their corresponding interrupt vector fires.
 *
 * \param twi       Base address of the TWI peripheral (i.e. &TWIC).
 * \param instance  Pointer to the TWI instance struct corresponding to the
 *                  given TWI peripheral.
 */
static void twi_master_interrupt_handler(volatile avr32_twim_t *const twi,
                struct twi_master_instance *instance)
{
        uint32_t status = twi->sr;
        const twi_package_t *current_package = instance->current_transfer;

        if (status & (AVR32_TWIM_SR_ANAK_MASK | AVR32_TWIM_SR_DNAK_MASK | AVR32_TWIM_SR_ARBLST_MASK)) {
                instance->status = (status & AVR32_TWIM_IER_ARBLST_MASK) ? ERR_BUSY : ERR_IO_ERROR;
                twi->CMDR.valid = false;
                twi->scr = -1;
                twi->idr = -1;
        }
        else if (status & AVR32_TWIM_SR_RXRDY_MASK) {           
                uint8_t *data = current_package->buffer;
                data[instance->transferred_length++] = twi->rhr;
        }
        else if (status & AVR32_TWIM_SR_TXRDY_MASK) {
                if (instance->sent_address_bytes < current_package->rw_address_length) {
                        uint8_t *data = (uint8_t *)¤t_package->rw_address;
                        twi->thr = data[instance->sent_address_bytes++];
                }
                else if (instance->transferred_length < current_package->length) {
                        uint8_t *data = current_package->buffer;
                        twi->thr = data[instance->transferred_length++];                        
                }
        }
        else if (status & AVR32_TWIM_SR_IDLE_MASK) {
                if (instance->transferred_length == current_package->length) {
                        instance->status = STATUS_OK;
                }       
        }
        
        /* Fire user callback if one has been registered and the transfer is
         * no longer in progress. */
        if ((instance->status != OPERATION_IN_PROGRESS) && instance->callback)
                instance->callback(twi);        
}

static inline status_code_t twi_master_set_baud(volatile avr32_twim_t *const twi,
                const uint32_t baud)
{
        uint32_t f_prescaled;
        uint8_t cwgr_exp = 0;
        
        f_prescaled = (sysclk_get_pba_hz() / baud / 2);

        // f_prescaled must fit in 8 bits, cwgr_exp must fit in 3 bits
        while ((f_prescaled > 0xFF) && (cwgr_exp <= 0x7)) {
                // increase clock divider
                cwgr_exp++;

                // divide f_prescaled value
                f_prescaled /= 2;
        }
        
        if (cwgr_exp > 0x7) {
                return ERR_INVALID_ARG;
        }

        // set clock waveform generator register
        twi->cwgr = ((f_prescaled/2) << AVR32_TWIM_CWGR_LOW_OFFSET)
                        | ((f_prescaled - f_prescaled/2) << AVR32_TWIM_CWGR_HIGH_OFFSET)
                        | (   cwgr_exp << AVR32_TWIM_CWGR_EXP_OFFSET)
                        | (          0 << AVR32_TWIM_CWGR_DATA_OFFSET)
                        | (f_prescaled << AVR32_TWIM_CWGR_STASTO_OFFSET);
        
        return STATUS_OK;
}

/**
 * \brief Initializes the TWI master module.
 *
 *  Initializes the given TWI peripheral, ready for use in master mode. 
 *
 * \param twi       Base address of the TWI peripheral (i.e. &AVR32_TWIM0).
 * \param baud      Baud rate of the TWI bus, in Hz.
 * \param callback  Function to call when the bus status changes.
 *
 * \return Current status of the corresponding TWI module, a value from
 *         \ref status_code_t.
 */
status_code_t twi_master_init(volatile avr32_twim_t *const twi,
                const uint32_t baud, const twi_master_callback_t callback)
{
        struct twi_master_instance *instance = twi_master_instance_from_module(twi);

        if (!baud) {
                return ERR_INVALID_ARG;
        }
        
        irqflags_t flags = cpu_irq_save();

        twi->idr = -1;
        twi->cr  = AVR32_TWIM_CR_MEN_MASK;
        twi->cr  = AVR32_TWIM_CR_SWRST_MASK;
        twi->scr = -1;
        
        if (twi_master_set_baud(twi, baud) != STATUS_OK) {
                return ERR_INVALID_ARG;
        }
        
        twi_master_register_int_handlers();
        
        instance->status = STATUS_OK;
        instance->callback = callback;

        cpu_irq_restore(flags);

        return STATUS_OK;
}

/**
 * \brief Retrieves the current status of the TWI master module.
 *
 * \param twi       Base address of the TWI peripheral (i.e. &AVR32_TWIM0).
 *
 * \return Current status of the corresponding TWI module, a value from
 *         \ref status_code_t.
 */
status_code_t twi_master_get_status(volatile avr32_twim_t *const twi)
{
        status_code_t status;
        
        irqflags_t flags = cpu_irq_save();

        status = twi_master_instance_from_module(twi)->status;

        cpu_irq_restore(flags);
        
        return status;  
}

/**
 * \brief Begins a transfer on the given master TWI bus.
 *
 *  Starts an asynchronous transfer on the given TWI master bus, with the given
 *  package.
 *
 * \param twi       Base address of the TWI peripheral (i.e. &AVR32_TWIM0).
 * \param package   Pointer to a TWI package to transfer.
 * \param read      Transfer direction, true to read, or false write.
 *
 * \return Current status of the corresponding TWI module, a value from
 *         \ref status_code_t.
 */
status_code_t twi_master_transfer(volatile avr32_twim_t *const twi,
        const twi_package_t *package, const bool read)
{
        status_code_t status;
        
        /* Do a sanity check on the arguments. */
        if ((twi == NULL) || (package == NULL)) {
                return ERR_INVALID_ARG;
        }

        struct twi_master_instance *instance = twi_master_instance_from_module(twi);
        
        irqflags_t flags = cpu_irq_save();
        
        /* Only start a new transfer if the bus is idle */
        if (instance->status != OPERATION_IN_PROGRESS) {
                instance->status = OPERATION_IN_PROGRESS;
                instance->current_transfer = package;
                instance->sent_address_bytes = 0;
                instance->transferred_length = 0;
                instance->read = read;
                
                twi->cr = AVR32_TWIM_CR_MEN_MASK;
                twi->cr = AVR32_TWIM_CR_SWRST_MASK;
                twi->cr = AVR32_TWIM_CR_MDIS_MASK;

                if (read) {
                        twi->cmdr = (package->chip_address << AVR32_TWIM_CMDR_SADR_OFFSET)
                                | (package->length << AVR32_TWIM_CMDR_NBYTES_OFFSET)
                                | (AVR32_TWIM_CMDR_VALID_MASK)
                                | (AVR32_TWIM_CMDR_START_MASK)
                                | (AVR32_TWIM_CMDR_STOP_MASK)
                                | (AVR32_TWIM_CMDR_READ_MASK);

                        if (package->rw_address_length) {
                                twi->ncmdr = twi->cmdr;
                                twi->cmdr = (package->chip_address << AVR32_TWIM_CMDR_SADR_OFFSET)
                                        | (package->rw_address_length << AVR32_TWIM_CMDR_NBYTES_OFFSET)
                                        | (AVR32_TWIM_CMDR_VALID_MASK)
                                        | (AVR32_TWIM_CMDR_START_MASK);
                        }
                }
                else {
                        twi->cmdr = (package->chip_address << AVR32_TWIM_CMDR_SADR_OFFSET)
                                | ((package->rw_address_length + package->length) << AVR32_TWIM_CMDR_NBYTES_OFFSET)
                                | (AVR32_TWIM_CMDR_VALID_MASK)
                                | (AVR32_TWIM_CMDR_START_MASK)
                                | (AVR32_TWIM_CMDR_STOP_MASK);
                }
                
                twi->ier = AVR32_TWIM_IER_ANAK_MASK | AVR32_TWIM_IER_DNAK_MASK |
                        AVR32_TWIM_IER_RXRDY_MASK | AVR32_TWIM_IER_TXRDY_MASK | AVR32_TWIM_IER_IDLE_MASK;
                twi->cr = AVR32_TWIM_CR_MEN_MASK;
        }
        
        /* Save status before unlocking global interrupts to preserve atomicity */
        status = instance->status;
        cpu_irq_restore(flags);

        return status;
}

/**
 * \brief Registers a callback for a given TWI module.
 *
 * \param twi       Base address of the TWI peripheral (i.e. &AVR32_TWIM0).
 * \param callback  Function to call when the bus status changes.
 */
void twi_master_set_callback(volatile avr32_twim_t *const twi,
                const twi_master_callback_t callback)
{
        twi_master_instance_from_module(twi)->callback = callback;
}

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh, and the header:

#ifndef _TWIM_H_
#define _TWIM_H_

/**
 * \defgroup group_xmega_drivers_twi_twim TWI Master
 *
 * \ingroup group_xmega_drivers_twi
 *
 *  @{
 */

#ifdef __cplusplus
extern "C" {
#endif

#include 
#include 
#include 
#include 

#include "conf_twim.h"

/** \brief TWI master bus package struct
 *
 *  Package descriptor for a TWI master packet to transfer on the bus.
 */
typedef struct {
        /*! Address of the slave to communicate with on the bus. */
        uint8_t chip_address;
        
        /*! Logical address within the slave. */
        uint32_t rw_address;
        
        /*! Length of the logical address within the slave to send. */
        uint8_t rw_address_length;

        /*! Pointer to a buffer where the data to transfer is located. */
        void *buffer;

        /*! Length of the data to transfer on the bus. */
        uint16_t length;
} twi_package_t;

/**
 * \brief Callback function type for a TWI master asynchronous callback.
 *
 * \param twi       Base address of the TWI peripheral (i.e. &TWIC).
 */
typedef void (*twi_master_callback_t)(volatile avr32_twim_t *const twi);

status_code_t twi_master_init(volatile avr32_twim_t *const twi, const uint32_t baud,
                const twi_master_callback_t callback);

status_code_t twi_master_get_status(volatile avr32_twim_t *const twi);

status_code_t twi_master_transfer(volatile avr32_twim_t *const twi,
        const twi_package_t *package, const bool read);

/**
 * \brief Begins a read transfer on the given master TWI bus.
 *
 *  Starts an asynchronous read transfer on the given TWI master bus, with the
 *  given package.
 *
 * \param twi       Base address of the TWI peripheral (i.e. &TWIC).
 * \param package   Pointer to a TWI package to transfer.
 *
 * \return Current status of the corresponding TWI module, a value from
 *         \ref status_code_t.
 */
static inline status_code_t twi_master_read(volatile avr32_twim_t *const twi,
        const twi_package_t *package)
{
        return twi_master_transfer(twi, package, true);
}

/**
 * \brief Begins a write transfer on the given master TWI bus.
 *
 *  Starts an asynchronous write transfer on the given TWI master bus, with the
 *  given package.
 *
 * \param twi       Base address of the TWI peripheral (i.e. &TWIC).
 * \param package   Pointer to a TWI package to transfer.
 *
 * \return Current status of the corresponding TWI module, a value from
 *         \ref status_code_t.
 */
static inline status_code_t twi_master_write(volatile avr32_twim_t *const twi,
        const twi_package_t *package)
{
        return twi_master_transfer(twi, package, false);
}

void twi_master_set_callback(volatile avr32_twim_t *const twi, twi_master_callback_t callback);

#ifdef __cplusplus
}
#endif

/** @} */

#endif /* _TWIM_H_ */

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Dean

Can (did?) you attach the patch to the ASF bugzilla?

thanks :)
-sma

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The full code is in branch bug1932 - Christian is reviewing it and Xavier will merge it once it's been signed off on.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh whoops, I thought this was the DFLL bug thread. This is actually in bug1915, where I was attempting to revamp all the TWI drivers, but only got through the XMEGA TWIM/TWIS and UC3 TWIM before I heard that Nantes was working on their own version and abandoned my own.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Dean for the example !

Based on that I have posted a project on AVR Freakfor TWIM for UC3C in C++ fully interrupt based that support multiple TWI device for the same chip :

https://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=viewItem&item_type=project&item_id=3558

Guillaume Charland

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

reviving a quite old post, because I think it is relevant still today.

 

The code for TWI Master from ASF, as of version 3.28 (November 2015), is still (!) hardcoded for just the first TWIM0 interface. When you add the TWIM module in Atmel Studio, by using the ASF Wizard, it imports a file called conf_twim.h, with #defines such as:

 

#define CONF_TWIM_IRQ_LINE     AVR32_TWIM0_IRQ
#define CONF_TWIM_IRQ_GROUP    AVR32_TWIM0_GROUP
#define CONF_TWIM_IRQ_LEVEL    1

 

You can change those to either AVR32_TWIM0AVR32_TWIM1 or AVR32_TWIM2. But there isn't any mechanism to define more than one Master interface at the same time. So only one TWIM IRQ will be registered to attend for interruptions, and the others will fire the default interruption handler, making the application to stall.

 

Then there is the bug report that was submitted by mjordan:

 

this bug seems that was resolved on July 2012, however in August 2012 it was reopened again and has been stagnant since then (apart from someone being added to the notification email list). Also, back then, Laurent Le Goffic already mentioned that a development branch was being validated for inclusion in the main trunk; however it seems that the solution was never published and I doubt it's even possible to access the mentioned branch.

 

Does anyone who reads this today know what is the current state? Maybe there were some news in this time.

 

 

Also about the proposed implementations,

 

abcminiuser wrote:
I was toying around with this on my spare time - attached is a preliminary example and not fully tested AVR32 TWIM driver that supports asynchronous callbacks. Use it as an example only since I can't guarantee anything about it -- if it works for you great, but I'd rather you use it as a reference for your own fully tested driver.
 

 

Was this implementation used by any project around here? I bet after 3 years, bugs must have been found and this code has been updated...

 

Theguigz wrote:
Based on that I have posted a project on AVR Freakfor TWIM for UC3C in C++ fully interrupt based that support multiple TWI device for the same chip : https://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=viewItem&item_type=project&item_id=3558
 

 

I wanted to check the one from Theguigz but the link doesn't work anymore :-( does the old Academy projects even exist somewhere?

 

Regards

Last Edited: Wed. Nov 11, 2015 - 03:04 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
Hello. Is there a chance to fix the error