TWIHS + XDMAC

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

Hi all. The problem occurred when reading data through the TWIHS using XDMAC. The program given below:

Initialization of TWIHS interface.

uint32_t twi0_master_setup(twi_master_options_t *p_opt)
{
 stop_sent=0;
 twi0_port_state=-1;

 read_packet=0;
 write_packet=0; 
 
 uint32_t ul_sysclk;
 // Get system clock. 
 ul_sysclk = sysclk_get_cpu_hz(); 
 p_opt->master_clk = ul_sysclk;
 p_opt->smbus      = 0;
 
 sysclk_enable_peripheral_clock(ID_TWIHS0); 
 
 // Configure and enable interrupt of twi.
 NVIC_EnableIRQ((IRQn_Type)twi_id);
 NVIC_SetPriority((IRQn_Type)twi_id, TWI0_IRQ_level);
 twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK); 
 
 
 uint32_t status= twihs_master_init(twi_ptr, p_opt);
 if(status==TWIHS_SUCCESS) twi0_port_state=TWI_INIT;

 return status;
}
 /*Enable XDMA interrupt */
 NVIC_ClearPendingIRQ(XDMAC_IRQn);
 NVIC_SetPriority( XDMAC_IRQn ,1);
 NVIC_EnableIRQ(XDMAC_IRQn);
 
 twi_master_options_t opt = { opt.speed = 400000};
 twi0_master_setup(&opt);
void twi_master_r(twihs_packet_t *p_packet)
{
 twi_ptr->TWIHS_SR;
 read_packet=p_packet;
 write_packet=0;
 /* Check argument */
 if ((read_packet->length == 0) || (read_packet->buffer==0))
 {
  TWI_status=TWIHS_INVALID_ARGUMENT;
  return;
 }

 /* Set read mode, slave address and 3 internal address byte lengths */
 twi_ptr->TWIHS_MMR = 0;
 twi_ptr->TWIHS_MMR = TWIHS_MMR_MREAD | TWIHS_MMR_DADR(read_packet->chip) |
 ((p_packet->addr_length << TWIHS_MMR_IADRSZ_Pos) &
 TWIHS_MMR_IADRSZ_Msk);

 /* Set internal address for remote chip */
 twi_ptr->TWIHS_IADR = 0;
 twi_ptr->TWIHS_IADR = twihs_mk_addr(read_packet->addr, read_packet->addr_length);
 
 twi0_port_state=TWI_RECEIVING;

 if (read_packet->length > 2) 
 { 
  //Initialize RX channel config 
  xdmac_TWI0_RX_channel_cfg.mbr_ubc =(XDMAC_UBC_NVIEW_NDV0 |
           XDMAC_UBC_NDE_FETCH_DIS | //Next Descriptor Disable
           XDMAC_UBC_NDEN_UPDATED) | //Destination parameters are updated when the descriptor is retrieved 
           (read_packet->length-1);  

  xdmac_TWI0_RX_channel_cfg.mbr_sa = (uint32_t)twi_ptr->TWIHS_RHR; //Source Address Member
  xdmac_TWI0_RX_channel_cfg.mbr_da = (uint32_t)read_packet->buffer; //Destination Address Member
  xdmac_TWI0_RX_channel_cfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN  | //(XDMAC_CC) Synchronized mode (Peripheral to Memory or Memory to Peripheral Transfer).
           XDMAC_CC_MEMSET_NORMAL_MODE | //(XDMAC_CC) Memset is not activated
           XDMAC_CC_MBSIZE_SINGLE  | //(XDMAC_CC) The memory burst size is set to one
           XDMAC_CC_DSYNC_PER2MEM  | //(XDMAC_CC) Peripheral to Memory transfer
           XDMAC_CC_CSIZE_CHK_1  | //(XDMAC_CC) 1 data transferred
           XDMAC_CC_DWIDTH_BYTE  | //(XDMAC_CC) The data size is set to 8 bits
           XDMAC_CC_SIF_AHB_IF1  | //(XDMAC_CC) The data is read through the system bus interface 1
           XDMAC_CC_DIF_AHB_IF0  | //(XDMAC_CC) The data is written through the system bus interface 0
           XDMAC_CC_SAM_FIXED_AM  | //(XDMAC_CC) The address remains unchanged.
           XDMAC_CC_DAM_INCREMENTED_AM | //(XDMAC_CC) The addressing mode is incremented (the increment size is set to the data size).
           XDMAC_CC_PERID(XDAMC_CHANNEL_HWID_TWIHS0_RX);    //(XDMAC_CC) Channel x Peripheral Identifier

  xdmac_TWI0_RX_channel_cfg.mbr_bc = 0 ;        //Block Control Member
  xdmac_TWI0_RX_channel_cfg.mbr_ds =  0;        //Data Stride Member
  xdmac_TWI0_RX_channel_cfg.mbr_sus = 0;        //Source Microblock Stride Member.
  xdmac_TWI0_RX_channel_cfg.mbr_dus = 0;        //Destination Microblock Stride Member.
  xdmac_configure_transfer(XDMAC, XDMA_TWI0_RX_CH, &xdmac_TWI0_RX_channel_cfg);
  
  xdmac_channel_set_descriptor_control(XDMAC, XDMA_TWI0_RX_CH, 0); 
   
  xdmac_enable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_channel_enable_interrupt(XDMAC, XDMA_TWI0_RX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
  xdmac_channel_enable(XDMAC, XDMA_TWI0_RX_CH);
 
  twihs_enable_interrupt(twi_ptr, TWIHS_IER_TXCOMP | TWIHS_IER_NACK | TWIHS_IER_OVRE| TWIHS_IER_ARBLST);
 }
 else
 {
  twihs_enable_interrupt(twi_ptr, TWIHS_IER_NACK | TWIHS_IER_RXRDY | TWIHS_IER_ARBLST);  
 }
 
 /* Send a START condition */
 if (read_packet->length == 1)
 {
  twi_ptr->TWIHS_CR = TWIHS_CR_START | TWIHS_CR_STOP;
  stop_sent = 1;
 } else
 {
  twi_ptr->TWIHS_CR = TWIHS_CR_START;
  stop_sent = 0;
 } 
 
 TWI0_StartTimeOut=true;
 
}

The interrupt handler of XDMAC

void XDMAC_Handler(void)
{
 uint32_t dma_status;

 dma_status = xdmac_channel_get_interrupt_status(XDMAC, XDMA_TWI0_RX_CH);
 if (dma_status & XDMAC_CIS_BIS)                 (XDMAC_CIS) End of Block Interrupt Status Bit
 {
  XDMA_TWI0_ENDRX();
  XDMAC_TWIHS_RX_CIE_BIE_count++;
 }

 if (dma_status & XDMAC_CIE_DIE)                 //(XDMAC_CIE) End of Disable Interrupt Enable Bit
 {
  XDMAC_TWIHS_RX_CIE_DIE_count++;    
 }

 if (dma_status & XDMAC_CIE_FIE)     //(XDMAC_CIE) End of Flush Interrupt Enable Bit 
 {
  XDMAC_TWIHS_RX_CIE_FIE_count++;
 }

 if (dma_status & XDMAC_CIE_RBIE)    //(XDMAC_CIE) Read Bus Error Interrupt Enable Bit
 {
  XDMAC_TWIHS_RX_CIE_RBIE_count++;
 }

 if (dma_status & XDMAC_CIE_WBIE)    //(XDMAC_CIE) Write Bus Error Interrupt Enable Bit
 {
  XDMAC_TWIHS_RX_CIE_WBIE_count++;
 }

 if (dma_status & XDMAC_CIE_ROIE)    //(XDMAC_CIE) Request Overflow Error Interrupt Enable Bit 
 {
  XDMAC_TWIHS_RX_CIE_ROIE_count++;
 } 

}


void XDMA_TWI0_ENDRX()
{
    xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
    xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
    xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
    XDMA_TWI0_ENDRX_count++;
    twihs_enable_interrupt(twi_ptr, TWIHS_IER_NACK | TWIHS_IER_RXRDY );
    twi_ptr->TWIHS_CR = TWIHS_CR_STOP;
}

Read one byte of data, and the transfer stops.

Logic analyzer detects a NACK condition, but the interrupt does not occur.

What did I do wrong?

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

I did not study your code but I think your problem is that NACK condition is detected by the TWI controller and if you configure a NACK interrupt, e.g. using twihs_enable_interrupt(TWIM0, TWIHS_IER_NACK);
you must also setup an TWI interrupt handler to handle the NACK interrupt.

I tried to implement TWI with DMA and there are several posts from me to this subject in this forum. Maybe this can help you.

SAME newbie

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

The interrupt handler TWIHS have available in my program.

void TWIHS0_Handler(void)
{
 uint8_t *r_buffer = (uint8_t *)read_packet->buffer;
 uint8_t *w_buffer = (uint8_t *)write_packet->buffer;
 // Read TWI status.
 TWI_ISR_status = twi_ptr->TWIHS_SR;
 TWI_ISR_mask=twi_ptr->TWIHS_IMR;
 
 if ((TWI_ISR_status & TWIHS_SR_NACK) && (TWI_ISR_mask & TWIHS_IMR_NACK))
 {
  TWI_status=TWIHS_RECEIVE_NACK;
  twi0_port_state=TWI_FAULT;
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_TX_CH);  
  
  twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
  DBC_u.DBC_S.TWI_ErrorCount.NACK_Ctr++;
  TWI0_StartTimeOut=false;
  TWI0_TC=0;
  return;
 } 
 
 if ((TWI_ISR_status & TWIHS_SR_ARBLST) && (TWI_ISR_mask & TWIHS_IMR_ARBLST))
 {
  TWI_status=TWIHS_ARBITRATION_LOST;
  twi0_port_state=TWI_FAULT;
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_TX_CH);  
  twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
  DBC_u.DBC_S.TWI_ErrorCount.ARBITRATION_LOST_Ctr++;
  TWI0_StartTimeOut=false;
  TWI0_TC=0;
  return;
 } 
 
 switch(twi0_port_state)
 {
  case TWI_RECEIVING:
  {
   if ((TWI_ISR_status & TWIHS_SR_OVRE) && (TWI_ISR_mask & TWIHS_IMR_OVRE))
   {
    TWI_status=TWIHS_RECEIVE_OVERRUN;
    twi0_port_state=TWI_FAULT;
    xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
    xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
    xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
    xdmac_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH);
    xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
    xdmac_channel_disable(XDMAC, XDMA_TWI0_TX_CH);    
    twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
    DBC_u.DBC_S.TWI_ErrorCount.OverErr_Ctr++;
    TWI0_StartTimeOut=false;
    TWI0_TC=0;
    return;
   }   
   
 
   if ((TWI_ISR_status & TWIHS_SR_RXRDY)&& (TWI_ISR_mask & TWIHS_IMR_RXRDY))
   {
    if (!stop_sent) 
    {
     twi_ptr->TWIHS_CR = TWIHS_CR_STOP;
     stop_sent = 1;
    } 
     twihs_disable_interrupt(twi_ptr, TWIHS_IDR_RXRDY);
     twihs_enable_interrupt(twi_ptr, TWIHS_IER_NACK | TWIHS_IER_TXCOMP );
     if(r_buffer!=0)
     {
       r_buffer[read_packet->length-1] = twi_ptr->TWIHS_RHR; 
     }
     else twi_ptr->TWIHS_RHR;    
     
   }//if ((TWI_ISR_status & TWI_SR_RXRDY)&& (TWI_ISR_mask & TWI_IMR_RXRDY))
 
   if ((twi_ptr->TWIHS_SR & TWIHS_SR_TXCOMP)&& (TWI_ISR_mask & TWIHS_IMR_TXCOMP))
   {
    twi_ptr->TWIHS_SR;
    TWI_status=TWIHS_SUCCESS;
    twi0_port_state=TWI_COMPLITE;
    twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
    TWI0_StartTimeOut=false;
    TWI0_TC=0;
    
   }   
  }//case TWI_RECEIVING:
  break;
  case TWI_TRANSMITTING:
  {  
   if ((TWI_ISR_status & TWIHS_SR_TXRDY)&& (TWI_ISR_mask & TWIHS_IMR_TXRDY))
   {
    twihs_disable_interrupt(twi_ptr, TWIHS_IDR_TXRDY);
    twihs_enable_interrupt(twi_ptr, TWIHS_IER_NACK | TWIHS_IER_TXCOMP);
   
    twi_ptr->TWIHS_CR = TWIHS_CR_STOP;
    if(w_buffer!=0) twi_ptr->TWIHS_THR = w_buffer[write_packet->length-1];
    else            twi_ptr->TWIHS_THR =0;
   } 
   
   if ((twi_ptr->TWIHS_SR & TWIHS_SR_TXCOMP)&& (TWI_ISR_mask & TWIHS_IMR_TXCOMP))
   {
    TWI_status=TWIHS_SUCCESS;
    twi0_port_state=TWI_COMPLITE;
    twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
    TWI0_StartTimeOut=false;
    TWI0_TC=0;
   }  
     
  }//case TWI_TRANSMITTING:
  break;
 }//switch(twi0_port_state)

 
}

If you disconnect the external device from the port TWIHS an interrupt NACK occurs.

When reading data using XDMAC interrupt  does not occur and the transfer stops.

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

I think the problem here is configuring the DMA channel.

A similar program worked on ATSAM4E16E, but only with the PDC controller.

regjoe if you have a working program TWIHS + XDMAC, please put on the forum settings XDMAC.

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

For unknown reason I cannot use the "add code" feature of this site anymore. It does not accept copy&paste. Sorry for bad readability of this post.

void twi_xdmac_rx_configure(Twihs *const p_twihs, uint8_t twi_xdmac_rx_ch_hwid, uint8_t xdmac_rx_ch, uint32_t *buffer, uint32_t size, uint8_t width)
{
    uint32_t xdmaint;

    xdmaint = (XDMAC_CIE_BIE |
        XDMAC_CIE_DIE   |
        XDMAC_CIE_FIE   |
        XDMAC_CIE_RBIE  |
        XDMAC_CIE_WBIE  |
        XDMAC_CIE_ROIE);

    /* Initialize channel config for receiver */
    xdmac_rx_cfg.mbr_ubc = size;

    xdmac_rx_cfg.mbr_da = (uint32_t)buffer;                    // get address of pointer to destination buffer
    xdmac_rx_cfg.mbr_sa = (uint32_t)&(p_twihs->TWIHS_RHR);    // get TWI receive register address

    xdmac_rx_cfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN |
        XDMAC_CC_MBSIZE_SINGLE |
        XDMAC_CC_DSYNC_PER2MEM |
        XDMAC_CC_CSIZE_CHK_1 |
        ((width == 4) ? XDMAC_CC_DWIDTH_WORD : XDMAC_CC_DWIDTH_BYTE) |
        XDMAC_CC_SIF_AHB_IF1 |
        XDMAC_CC_DIF_AHB_IF0 |
        XDMAC_CC_SAM_FIXED_AM |
        XDMAC_CC_DAM_INCREMENTED_AM |
        XDMAC_CC_PERID(twi_xdmac_rx_ch_hwid);

    xdmac_rx_cfg.mbr_bc  = 0;
    xdmac_rx_cfg.mbr_ds  = 0;
    xdmac_rx_cfg.mbr_sus = 0;
    xdmac_rx_cfg.mbr_dus = 0;

    xdmac_configure_transfer(XDMAC, xdmac_rx_ch, &xdmac_rx_cfg);

    xdmac_channel_set_descriptor_control(XDMAC, xdmac_rx_ch, 0);

    xdmac_channel_enable_interrupt(XDMAC, xdmac_rx_ch, xdmaint);
    xdmac_channel_enable(XDMAC, xdmac_rx_ch);
    xdmac_enable_interrupt(XDMAC, xdmac_rx_ch);

#if 1
    /*Enable XDMAC interrupt */
    NVIC_EnableIRQ(XDMAC_IRQn);
#endif

}

 

SAME newbie

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

Thanks for the help, but the problem is not initializing the DMA channel.

I redid the program, so that was the opportunity to work TWIHS with the DMA channel and without DMA channel.

#ifdef CONF_BOARD_TWIHS0
#define twi_ptr TWIHS0    
#define twi_id ID_TWIHS0      
#endif

#ifdef CONF_BOARD_TWIHS2
#define twi_ptr TWIHS2    
#define twi_id ID_TWIHS2      
#endif

static uint32_t read_packet_cnt=0;

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

void twi_master_r(twihs_packet_t *p_packet)
{
 twi_ptr->TWIHS_SR;
 read_packet=p_packet;
 write_packet=0;
 // Check argument 
 if ((read_packet->length == 0) || (read_packet->buffer==0))
 {
  TWI_status=TWIHS_INVALID_ARGUMENT;
  return;
 }

 // Set read mode, slave address and 3 internal address byte lengths 
 twi_ptr->TWIHS_MMR = 0;
 twi_ptr->TWIHS_MMR = TWIHS_MMR_MREAD | TWIHS_MMR_DADR(read_packet->chip) |
 ((p_packet->addr_length << TWIHS_MMR_IADRSZ_Pos) &
 TWIHS_MMR_IADRSZ_Msk);

 // Set internal address for remote chip 
 twi_ptr->TWIHS_IADR = 0;
 twi_ptr->TWIHS_IADR = twihs_mk_addr(read_packet->addr, read_packet->addr_length);
 
 twi_port_state=TWI_RECEIVING;
 read_packet_cnt=read_packet->length;
#ifdef CONF_BOARD_TWI_DMA_Enable
 if (read_packet->length > 2) 
 { 
  //Initialize RX channel config 
  xdmac_TWI0_RX_channel_cfg.mbr_ubc = (read_packet->length-2);  

  xdmac_TWI0_RX_channel_cfg.mbr_sa = (uint32_t)twi_ptr->TWIHS_RHR; //Source Address Member
  xdmac_TWI0_RX_channel_cfg.mbr_da = (uint32_t)read_packet->buffer; //Destination Address Member
  xdmac_TWI0_RX_channel_cfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN  | //(XDMAC_CC) Synchronized mode (Peripheral to Memory or Memory to Peripheral Transfer).
           XDMAC_CC_MEMSET_NORMAL_MODE | //(XDMAC_CC) Memset is not activated
           XDMAC_CC_MBSIZE_SINGLE  | //(XDMAC_CC) The memory burst size is set to one
           XDMAC_CC_DSYNC_PER2MEM  | //(XDMAC_CC) Peripheral to Memory transfer
           XDMAC_CC_CSIZE_CHK_1  | //(XDMAC_CC) 1 data transferred
           XDMAC_CC_DWIDTH_BYTE  | //(XDMAC_CC) The data size is set to 8 bits
           XDMAC_CC_SIF_AHB_IF1  | //(XDMAC_CC) The data is read through the system bus interface 1
           XDMAC_CC_DIF_AHB_IF0  | //(XDMAC_CC) The data is written through the system bus interface 0
           XDMAC_CC_SAM_FIXED_AM  | //(XDMAC_CC) The address remains unchanged.
           XDMAC_CC_DAM_INCREMENTED_AM | //(XDMAC_CC) The addressing mode is incremented (the increment size is set to the data size).
#ifdef CONF_BOARD_TWIHS0
           XDMAC_CC_PERID(XDAMC_CHANNEL_HWID_TWIHS0_RX);    //(XDMAC_CC) Channel x Peripheral Identifier
#endif
#ifdef CONF_BOARD_TWIHS2
           XDMAC_CC_PERID(XDAMC_CHANNEL_HWID_TWIHS2_RX);    //(XDMAC_CC) Channel x Peripheral Identifier
#endif


  xdmac_TWI0_RX_channel_cfg.mbr_bc = 0 ;        //Block Control Member
  xdmac_TWI0_RX_channel_cfg.mbr_ds =  0;        //Data Stride Member
  xdmac_TWI0_RX_channel_cfg.mbr_sus = 0;        //Source Microblock Stride Member.
  xdmac_TWI0_RX_channel_cfg.mbr_dus = 0;        //Destination Microblock Stride Member.
  xdmac_configure_transfer(XDMAC, XDMA_TWI0_RX_CH, &xdmac_TWI0_RX_channel_cfg);
  
  xdmac_channel_set_descriptor_control(XDMAC, XDMA_TWI0_RX_CH, 0); //Destination parameters are updated when the descriptor is retrieved.
   
  
      uint32_t xdmaint;

      xdmaint = (XDMAC_CIE_BIE |//End of Block Interrupt Enable Bit
      XDMAC_CIE_DIE   |
      XDMAC_CIE_FIE   |
      XDMAC_CIE_RBIE  |
      XDMAC_CIE_WBIE  |
      XDMAC_CIE_ROIE);

  xdmac_channel_enable_interrupt(XDMAC, XDMA_TWI0_RX_CH, xdmaint );
  xdmac_channel_enable(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_enable_interrupt(XDMAC, XDMA_TWI0_RX_CH); 
 }


    twihs_enable_interrupt(twi_ptr, TWIHS_IER_NACK | TWIHS_IER_OVRE| TWIHS_IER_ARBLST);
#else
    twihs_enable_interrupt(twi_ptr, TWIHS_IER_NACK | TWIHS_IER_OVRE| TWIHS_IER_ARBLST| TWIHS_IER_RXRDY ); 
#endif
 
 // Send a START condition 
 if (read_packet->length == 1)
 {
  twi_ptr->TWIHS_CR = TWIHS_CR_START | TWIHS_CR_STOP;
  stop_sent = 1;
 } else
 {
  twi_ptr->TWIHS_CR = TWIHS_CR_START;
  stop_sent = 0;
 } 
 
 TWI0_StartTimeOut=true;
 
}
//--------------------------------------------------------------------------------
#ifdef CONF_BOARD_TWIHS0
void TWIHS0_Handler(void)
#endif
#ifdef CONF_BOARD_TWIHS2
void TWIHS2_Handler(void)
#endif
{
 uint8_t *r_buffer = (uint8_t *)read_packet->buffer;
 uint8_t *w_buffer = (uint8_t *)write_packet->buffer;
 // Read TWI status.
 TWI_ISR_status = twi_ptr->TWIHS_SR;
 TWI_ISR_mask=twi_ptr->TWIHS_IMR;
 
 if ((TWI_ISR_status & TWIHS_SR_NACK) && (TWI_ISR_mask & TWIHS_IMR_NACK))
 {
  TWI_status=TWIHS_RECEIVE_NACK;
  twi0_port_state=TWI_FAULT;
#ifdef CONF_BOARD_TWI_DMA_Enable
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, ALL_INTERRUPT_MASK);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH, ALL_INTERRUPT_MASK);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_TX_CH);  
#endif  
  twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
  DBC_u.DBC_S.TWI_ErrorCount[CurTask].NACK_Ctr++;
  TWI0_StartTimeOut=false;
  TWI0_TC=0;
  return;
 } 
 
 if ((TWI_ISR_status & TWIHS_SR_ARBLST) && (TWI_ISR_mask & TWIHS_IMR_ARBLST))
 {
  TWI_status=TWIHS_ARBITRATION_LOST;
  twi0_port_state=TWI_FAULT;

#ifdef CONF_BOARD_TWI_DMA_Enable
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, ALL_INTERRUPT_MASK);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
  xdmac_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH);
  xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH, ALL_INTERRUPT_MASK);
  xdmac_channel_disable(XDMAC, XDMA_TWI0_TX_CH);
#endif     
  twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);

  DBC_u.DBC_S.TWI_ErrorCount[CurTask].ARBITRATION_LOST_Ctr++;
  TWI0_StartTimeOut=false;
  TWI0_TC=0;
  return;
 } 
 
 switch(twi_port_state)
 {
  case TWI_RECEIVING:
  {
   if ((TWI_ISR_status & TWIHS_SR_OVRE) && (TWI_ISR_mask & TWIHS_IMR_OVRE))
   {
    TWI_status=TWIHS_RECEIVE_OVERRUN;
    twi0_port_state=TWI_FAULT;
#ifdef CONF_BOARD_TWI_DMA_Enable
    xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
    xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, ALL_INTERRUPT_MASK);
    xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
    xdmac_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH);
    xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_TX_CH, ALL_INTERRUPT_MASK);
    xdmac_channel_disable(XDMAC, XDMA_TWI0_TX_CH); 
#endif       
    twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
    DBC_u.DBC_S.TWI_ErrorCount[CurTask].OverErr_Ctr++;
    TWI0_StartTimeOut=false;
    TWI0_TC=0;
    return;
   }   
   
 
   if ((TWI_ISR_status & TWIHS_SR_RXRDY)&& (TWI_ISR_mask & TWIHS_IMR_RXRDY))
   {
    if ((!stop_sent) && (read_packet_cnt==1))
    {
     twi_ptr->TWIHS_CR = TWIHS_CR_STOP;
     stop_sent = 1;
     twihs_disable_interrupt(twi_ptr, TWIHS_IDR_RXRDY);
    } 
     
     if(r_buffer!=0)
     {
       r_buffer[read_packet->length-read_packet_cnt] = twi_ptr->TWIHS_RHR; 
       read_packet_cnt--;
     }
     else twi_ptr->TWIHS_RHR;  
     
     if(read_packet_cnt==0)
     {
      twihs_enable_interrupt(twi_ptr, TWIHS_IER_TXCOMP);  
      } 
     
   }//if ((TWI_ISR_status & TWI_SR_RXRDY)&& (TWI_ISR_mask & TWI_IMR_RXRDY))
 
   if ((twi_ptr->TWIHS_SR & TWIHS_SR_TXCOMP)&& (TWI_ISR_mask & TWIHS_IMR_TXCOMP))
   {
    twi_ptr->TWIHS_SR;
    TWI_status=TWIHS_SUCCESS;
    twi0_port_state=TWI_COMPLITE;
    twihs_disable_interrupt(twi_ptr, ALL_INTERRUPT_MASK);
    TWI0_StartTimeOut=false;
    TWI0_TC=0;
    
   }   
  }//case TWI_RECEIVING:

  break;
 }//switch(twi_port_state)

 
}

//-------------------------------------------------------------------------------------------------------------
void XDMA_TWI0_ENDRX() //The function is called in the interrupt handler XDMAC
{
    xdmac_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH);
    xdmac_channel_disable_interrupt(XDMAC, XDMA_TWI0_RX_CH, XDMAC_CIE_BIE /*End of Block Interrupt Enable Bit*/);
    xdmac_channel_disable(XDMAC, XDMA_TWI0_RX_CH);
    XDMA_TWI0_ENDRX_count++;
    
    read_packet_cnt=2;
    twihs_enable_interrupt(twi_ptr, TWIHS_IER_RXRDY );    
}

When TWIHS0 works without a DMA channel, then the packet is read completely.

When TWIHS0 works with DMA channel, then read 2 bytes of the packet, and the transfer stops.

 

The TWIHS interrupt handler is written according to the datasheet:

Data Receive with the DMA in Master Mode

The DMA transfer size must be defined with the buffer size minus 2. The two remaining characters must be

managed without DMA to ensure that the exact number of bytes are received regardless of system bus latency

conditions encountered during the end of buffer transfer period.

1. Initialize the DMA (channels, memory pointers, size -2, etc.);

2. Configure the Master mode (DADR, CKDIV, MREAD = 1, etc.) or Slave mode.

3. Enable the DMA.

4. (Master Only) Write the START bit in the TWIHS_CR to start the transfer.

5. Wait for the DMA status flag indicating that the buffer transfer is complete.

6. Disable the DMA.

7. Wait for the RXRDY flag in the TWIHS_SR.

8. Set the STOP bit in TWIHS_CR.

9. Read the penultimate character in TWIHS_RHR.

10. Wait for the RXRDY flag in the TWIHS_SR.

11. Read the last character in TWIHS_RHR.

12. (Only if peripheral clock must be disabled) Wait for the TXCOMP flag to be raised in TWIHS_SR.

 

It is assumed that this is a bug in the microcontroller.

I tried to change the DMA channels, but the result is the same.

I tried to use TWIHS2, pre-configuring the pins of the microcontroller, but TWIHS2 does not work.

/** TWI2 pins definition */
#define TWIHS2_DATA_GPIO   PIO_PD27_IDX
#define TWIHS2_DATA_FLAGS  (IOPORT_MODE_MUX_C)
#define TWIHS2_CLK_GPIO    PIO_PD28_IDX
#define TWIHS2_CLK_FLAGS   (IOPORT_MODE_MUX_C)

#ifdef CONF_BOARD_TWIHS2
ioport_set_pin_peripheral_mode(TWIHS2_DATA_GPIO, TWIHS2_DATA_FLAGS);
ioport_set_pin_peripheral_mode(TWIHS2_CLK_GPIO, TWIHS2_CLK_FLAGS);
#endif

 

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

MYA333 wrote:
It is assumed that this is a bug in the microcontroller.

What do you mean ? Any source for that assumption ?

The TWI + XDMAC implemention on the M7 MCU is not that powerful than for example the SPI + XDMAC implementation. TWI requires 'manual' programming of START and STOP condition therefore the hassle with xfer size less than 2 bytes.

Does DMA running with size > 2 bytes work on your side ?

SAME newbie

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

The packet size is 128 bytes.

As you can see from the program text, the conditions start and stop are set manually.

I mean, I probably got a defective MCU. I have previously worked with ATSAM4E16E and there TWI + PDC worked well.

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

MYA333 wrote:
If you disconnect the external device from the port TWIHS an interrupt NACK occurs. When reading data using XDMAC (NACK?) interrupt does not occur and the transfer stops (successfully?).

 

Going  back to the start of your post... Is this still your (main) problem ?

 

If so I consider this as two different situations which I try to explain in "Dinglish" == German-English :)

 

If you disconnect the slave from the bus (or use an invalid address) SDA is left high which means that arbitration/addressing of a slave failed. This is considered as an error and must be handled. Here the master should get NACK interrupt and the receive DMA hangs up (if already started) and must be killed/disabled.

 

If the master reads data from the slave a NACK condition is generated by the master to signal the slave that the slave shall stop sending bytes. Therefore you get only a DMA done interrupt which means that all data has been received. This is not an error and therefore no NACK interrupt is fired.

 

I got this from an I2C manual considering master read:

 

The master will continue sending out the clock pulses, but will release the SDA line, so that the slave can
transmit data. At the end of every byte of data, the master will send an ACK to the slave, letting the slave
know that it is ready for more data. Once the master has received the number of bytes it is expecting, it
will send a NACK, signaling to the slave to halt communications and release the bus. The master will
follow this up with a STOP condition.

 

If you unplug the slave after successful addressing in the middle of receive the master will most probably not detect this kind of error and read 0xFFs. But I'm not sure about that.

 

What do you think ?

 

SAME newbie

Last Edited: Fri. Nov 10, 2017 - 11:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The slave is connected (FRAM). Without the use of a DMA channel, what was written is then read.

I tried the same program to download into another SAME70-XPLD board.

The result is similar: without DMA channel it works, with DMA channel does not work.

AFEC with the DMA channel working well.