//------------------------------------------------------------------------------ /// Starts buffer transfer on the given channel /// \param channel Particular channel number. /// \param size Total transfer size in byte. /// \param callback Optional callback function. /// \param polling Polling channel status enable. //------------------------------------------------------------------------------ unsigned char DMAD_BufferTransfer(unsigned char channel, unsigned int size, DmaCallback callback, unsigned char polling) { DmaTransfer *pTransfer = &(dmad.transfers[channel]); // Check that no transfer is pending on the channel if (pTransfer-> transferSize > 0 ) { TRACE_ERROR("DAM transfer is already pending\n\r"); return DMAD_ERROR_BUSY; } pTransfer->status = DMAD_ERROR_BUSY; pTransfer->transferSize = size; pTransfer->callback = callback; if(!polling){ DMA_EnableIt(DMA_BTC << channel); } // Enable the channel. DMA_EnableChannel(channel); if(polling){ while ((DMA_GetChannelStatus() & (DMA_ENA << channel)) == (DMA_ENA << channel)); pTransfer->callback(); DMA_DisableChannel(channel); } return 0; }
//------------------------------------------------------------------------------ // Local functions //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ /// This handler function must be called by the DMAC interrupt service routine. /// Identifies which event was activated and calls the associated function. //------------------------------------------------------------------------------ void DMAD_Handler() { unsigned int status; unsigned char channel; DmaTransfer *pTransfer; status = DMA_GetStatus(); // Check if the buffer transfer completed is set. if(status & AT91C_BTC) { // Scan each channel status. for(channel = 0; channel < DMA_CHANNEL_NUM; channel++) { if(!(status & (DMA_BTC << channel))){ continue; } dmad.transfers[channel].transferSize -= dmad.transfers[channel].bufSize; // if next buffer is to be the last buffer in the transfer, then clear the automatic mode bit. if(dmad.transfers[channel].transferSize <= dmad.transfers[channel].bufSize) { DMA_ClearAutoMode(channel); } // Transfer finished if(dmad.transfers[channel].transferSize == 0) { pTransfer = &(dmad.transfers[channel]); pTransfer->callback(); DMA_DisableIt(DMA_BTC << channel); DMA_DisableChannel(channel); } else { // Write the KEEPON field to clear the STALL states. DMA_KeeponChannel(channel); } } } }
//------------------------------------------------------------------------------ /// Initializes the DMA controller. /// \param dwChannel Particular dwChannel number /// \param defaultHandler Using the default dmad interrupt handler. //------------------------------------------------------------------------------ extern void DMAD_Initialize( uint32_t dwChannel, uint32_t defaultHandler ) { uint32_t dwStatus ; uint32_t dwFlag ; /* Enable peripheral clock */ PMC_EnablePeripheral( ID_DMAC ) ; /* Read the dwChannel handler status to ensure the channel is a free channel */ dwStatus = DMA_GetChannelStatus( DMAC ) ; TRACE_INFO( "DMAD_Initialize dwChannel %x \n\r", dwChannel ) ; assert( (dwStatus & (1 << dwChannel)) == 0 ) ; /* Clear any pending interrupts on the channel */ DMA_GetStatus( DMAC ) ; /* Disable the channel */ DMA_DisableChannel( DMAC, dwChannel ) ; /* Disable the interrupt */ dwFlag = 0x3FFFFF ; DMA_DisableIt( DMAC, dwFlag ) ; /* Enable DMA */ DMA_Enable( DMAC ) ; if ( defaultHandler ) { NVIC_EnableIRQ( DMAC_IRQn ) ; } // Initialize transfer instance. dmad.transfers[dwChannel].transferSize = 0; }
//------------------------------------------------------------------------------ /// Starts buffer transfer on the given channel /// \param channel Particular channel number. /// \param size Total transfer size in byte. /// \param callback Optional callback function. /// \param polling Polling channel status enable. //------------------------------------------------------------------------------ U8 DMAD_BufferTransfer(U8 channel, U32 size, DmaCallback callback, U8 polling) { DmaTransfer *pTransfer = &(dmad.transfers[channel]); if (pTransfer-> transferSize > 0 ) // Check that no transfer is pending on the channel { DEBUG_MSG("DAM transfer is already pending"); return DMAD_ERROR_BUSY; } pTransfer->status = DMAD_ERROR_BUSY; pTransfer->transferSize = size; pTransfer->callback = callback; if(!polling) { DMA_EnableIt(0x1 << channel); } DMA_EnableChannel(channel); // Enable the channel. if(polling) { while ((DMA_GetChannelStatus() & (0x1 << channel)) == (0x1 << channel)); if (pTransfer->callback) { pTransfer->callback(); } pTransfer->transferSize = 0; DMA_DisableChannel(channel); } return 0; }
//------------------------------------------------------------------------------ /// Initializes the DMA controller. /// \param channel Particular channel number /// \param defaultHandler Using the default dmad interrupt handler. //------------------------------------------------------------------------------ void DMAD_Initialize(unsigned char channel, unsigned char defaultHandler) { unsigned int status; unsigned int flag; // Enable peripheral clock #if !defined(at91sam9rl64) AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_HDMA; #endif // Read the channel handler status to ensure the channel is a free channel. status = DMA_GetChannelStatus(); TRACE_INFO ("DMAD_Initialize channel %x \n\r", channel); SANITY_CHECK(!(status & (1 << channel))); // Clear any pending interrupts on the channel. DMA_GetStatus(); // Disble the channel. DMA_DisableChannel(channel); // Disable the interrupt flag = 0xffffff; DMA_DisableIt(flag); // Enable DMA. DMA_Enable(); if(defaultHandler) { IRQ_ConfigureIT(AT91C_ID_HDMA, 0, DMAD_Handler); IRQ_EnableIT(AT91C_ID_HDMA); } // Initialize transfer instance. dmad.transfers[channel].transferSize = 0; }
//------------------------------------------------------------------------------ /// Returns 1 if no transfer is currently pending on the given channel; /// otherwise, returns 0. /// \param channel Channel number. //------------------------------------------------------------------------------ unsigned char DMAD_IsFinished(unsigned char channel) { SANITY_CHECK(channel <= DMA_CHANNEL_NUM); if (dmad.transfers[channel].transferSize > 0) { return 0; } else { DMA_DisableChannel(channel); return 1; } }
//------------------------------------------------------------------------------ /// Returns 1 if no transfer is currently pending on the given channel; /// otherwise, returns 0. /// \param channel Channel number. //------------------------------------------------------------------------------ U8 DMAD_IsFinished(U8 channel) { if(dmad.transfers[channel].transferSize > 0) { return 0; } else { DMA_DisableChannel(channel); return 1; } }
//------------------------------------------------------------------------------ /// Returns 1 if no transfer is currently pending on the given dwChannel; /// otherwise, returns 0. /// \param dwChannel Channel number. //------------------------------------------------------------------------------ extern uint32_t DMAD_IsFinished( uint32_t dwChannel ) { assert( dwChannel <= DMA_CHANNEL_NUM ) ; if ( dmad.transfers[dwChannel].transferSize > 0 ) { return 0 ; } else { DMA_DisableChannel( DMAC, dwChannel ) ; return 1 ; } }
//------------------------------------------------------------------------------ // Local functions //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ /// This handler function must be called by the DMAC interrupt service routine. /// Identifies which event was activated and calls the associated function. //------------------------------------------------------------------------------ extern void DMAC_IrqHandler( void ) { uint32_t dwStatus ; uint32_t dwChannel ; DmaTransfer *pTransfer ; dwStatus = DMA_GetStatus( DMAC ) ; // Check if the buffer transfer completed is set. if ( dwStatus & (DMAC_EBCISR_BTC0|DMAC_EBCISR_BTC1|DMAC_EBCISR_BTC2|DMAC_EBCISR_BTC3|DMAC_EBCISR_BTC4|DMAC_EBCISR_BTC5) ) { // Scan each dwChannel status. for ( dwChannel = 0 ; dwChannel < DMA_CHANNEL_NUM ; dwChannel++ ) { if ( !(dwStatus & (DMAC_EBCISR_BTC0 << dwChannel)) ) { continue ; } dmad.transfers[dwChannel].transferSize -= dmad.transfers[dwChannel].bufSize ; // if next buffer is to be the last buffer in the transfer, then clear the automatic mode bit. if ( dmad.transfers[dwChannel].transferSize <= dmad.transfers[dwChannel].bufSize ) { DMA_ClearAutoMode( DMAC, dwChannel ) ; } // Transfer finished if ( dmad.transfers[dwChannel].transferSize == 0 ) { pTransfer = &(dmad.transfers[dwChannel]) ; pTransfer->callback() ; DMA_DisableIt( DMAC, DMAC_EBCIDR_BTC0 << dwChannel ) ; DMA_DisableChannel( DMAC, dwChannel ) ; } else { // Write the KEEPON field to clear the STALL states. DMA_KeeponChannel( DMAC, dwChannel ) ; } } } }
//------------------------------------------------------------------------------ /// Initializes the DMA controller. /// \param channel Particular channel number /// \param defaultHandler Using the default dmad interrupt handler. //------------------------------------------------------------------------------ void DMAD_Initialize(U8 channel, U8 defaultHandler) { // U32 status; U32 flag; // status = DMA_GetChannelStatus(); // Read the channel handler status to ensure the channel is a free channel. // DEBUG_MSG("DMAD_Initialize channel: %x, Status: %x", channel, status); DMA_GetStatus(); // Clear any pending interrupts on the channel. DMA_DisableChannel(channel); // Disble the channel. flag = 0xffffff; DMA_DisableIt(flag); // Disable the interrupt DMA_Enable(); // Enable DMA. if(defaultHandler) { IRQ_PeriConf(21, 0, DMAD_Handler); IRQ_PeriEn(21); } dmad.transfers[channel].transferSize = 0; // Initialize transfer instance. }
//------------------------------------------------------------------------------ /// Starts buffer transfer on the given dwChannel /// \param dwChannel Particular dwChannel number. /// \param size Total transfer size in byte. /// \param callback Optional callback function. /// \param polling Polling dwChannel status enable. //------------------------------------------------------------------------------ extern uint32_t DMAD_BufferTransfer( uint32_t dwChannel, uint32_t dwSize, DmaCallback callback, uint32_t dwPolling ) { DmaTransfer *pTransfer = &(dmad.transfers[dwChannel]) ; // Check that no transfer is pending on the dwChannel if ( pTransfer-> transferSize > 0 ) { TRACE_ERROR( "DAM transfer is already pending\n\r" ) ; return DMAD_ERROR_BUSY ; } pTransfer->status = DMAD_ERROR_BUSY ; pTransfer->transferSize = dwSize ; pTransfer->callback = callback ; if ( dwPolling == 0 ) { DMA_EnableIt( DMAC, DMA_BTC << dwChannel ) ; } // Enable the dwChannel. DMA_EnableChannel( DMAC, dwChannel ) ; if ( dwPolling != 0 ) { while ( (DMA_GetChannelStatus( DMAC ) & (DMAC_CHSR_ENA0 << dwChannel)) == (DMAC_CHSR_ENA0 << dwChannel)) ; if ( pTransfer->callback ) { pTransfer->callback() ; } pTransfer->transferSize = 0 ; DMA_DisableChannel( DMAC, dwChannel ) ; } return 0 ; }
//------------------------------------------------------------------------------ /// This handler function must be called by the DMAC interrupt service routine. /// Identifies which event was activated and calls the associated function. //------------------------------------------------------------------------------ void DMAD_Handler() { U32 status; U8 channel; DmaTransfer *pTransfer; status = DMA_GetStatus(); if(status & (0xFF)) // Check if the buffer transfer completed is set. { for(channel = 0; channel < 8; channel++) // Scan each channel status. { if(!(status & (0x1 << channel))) { continue; } dmad.transfers[channel].transferSize -= dmad.transfers[channel].bufSize; if(dmad.transfers[channel].transferSize <= dmad.transfers[channel].bufSize) // if next buffer is to be the last buffer in the transfer, then clear the automatic mode bit. { DMA_ClearAutoMode(channel); } if(dmad.transfers[channel].transferSize == 0) // Transfer finished { pTransfer = &(dmad.transfers[channel]); pTransfer->callback(); DMA_DisableIt(0x1 << channel); DMA_DisableChannel(channel); } else { DMA_KeeponChannel(channel); // Write the KEEPON field to clear the STALL states. } } } }
int main(void) { facilitatePowersaving(); // Configure switches PORTCFG.MPCMASK = 0xff; // Configure several PINxCTRL registers at the same time SWITCHPORT.PIN0CTRL = (SWITCHPORT.PIN0CTRL & ~PORT_OPC_gm) | PORT_OPC_PULLUP_gc; //Enable pull-up to get a defined level on the switches SWITCHPORT.DIRCLR = 0xff; // Set port as input // Configure LEDs PORTCFG.MPCMASK = 0xff; // Configure several PINxCTRL registers at the same time LEDPORT.PIN0CTRL = PORT_INVEN_bm; // Invert input to turn the leds on when port output value is 1 LEDPORT.DIRSET = 0xff; // Set port as output LEDPORT.OUT = 0x00; // Set initial value // Set up ADCB0 on PB0 to read temp sensor. More of this can be achieved by using driver from appnote AVR1300 PORTQ.PIN2CTRL = (PORTQ.PIN2CTRL & ~PORT_OPC_gm) | PORT_OPC_PULLDOWN_gc; // This pin must be grounded to "enable" NTC-resistor PORTB.DIRCLR = PIN0; PORTB.PIN0CTRL = (PORTB.PIN0CTRL & ~PORT_OPC_gm); ADC_CalibrationValues_Load(&ADCB); // Load factory calibration data for ADC ADCB.CH0.CTRL = (ADCB.CH0.CTRL & ~ADC_CH_INPUTMODE_gm) | ADC_CH_INPUTMODE_SINGLEENDED_gc; // Single ended input ADCB.CH0.MUXCTRL = (ADCB.CH0.MUXCTRL & ~ADC_CH_MUXPOS_gm) | ADC_CH_MUXPOS_PIN0_gc; // Pin 0 is input ADCB.REFCTRL = (ADCB.REFCTRL & ~ADC_REFSEL_gm) | ADC_REFSEL_VCC_gc; // Internal AVCC/1.6 as reference ADCB.CTRLB |= ADC_FREERUN_bm; // Free running mode ADCB.PRESCALER = (ADCB.PRESCALER & ~ADC_PRESCALER_gm) | ADC_PRESCALER_DIV512_gc; // Divide clock by 1024. ADCB.CTRLB = (ADCB.CTRLB & ~ADC_RESOLUTION_gm) | ADC_RESOLUTION_8BIT_gc; // Set 8 bit resolution ADCB.CTRLA |= ADC_ENABLE_bm; // Enable ADC // Set up DMA CH0 to transfer from ADC to LEDS. We only read low byte. DMA_Enable(); DMA_SetupBlock( &DMA.CH0, (void const *) &(ADCB.CH0RES), DMA_CH_SRCRELOAD_NONE_gc, DMA_CH_SRCDIR_FIXED_gc, (void const *) &(LEDPORT.OUT), DMA_CH_DESTRELOAD_NONE_gc, DMA_CH_DESTDIR_FIXED_gc, 1, DMA_CH_BURSTLEN_1BYTE_gc, 0, true ); DMA_EnableSingleShot( &DMA.CH0 ); DMA_SetTriggerSource( &DMA.CH0, DMA_CH_TRIGSRC_ADCB_CH0_gc ); // ADC Channel 0 is trigger source. // Set up interrupt on button 0, or else we can't come back from IDLE SWITCHPORT.INTCTRL = (SWITCHPORT.INTCTRL & ~PORT_INT0LVL_gm) | PORT_INT0LVL_LO_gc; SWITCHPORT.INT0MASK = SWITCHMASK_ACTIVE; SWITCHPORT.PIN0CTRL = (SWITCHPORT.PIN0CTRL & ~PORT_ISC_gm) | PORT_ISC_FALLING_gc; // Enable low interrupt level in PMIC and enable global interrupts. PMIC.CTRL |= PMIC_LOLVLEN_bm; sei(); // Main loop. while (1) { if ((SWITCHPORT.IN & SWITCHMASK_ACTIVE) == 0x00) { // Button 0 pressed. Enter ACTIVE mode again. DMA_DisableChannel( &DMA.CH0 ); SLEEP.CTRL &= ~SLEEP_SEN_bm; // Disable sleep. sleep() is now ineffective. } else if ((SWITCHPORT.IN & SWITCHMASK_IDLE) == 0x00) { // Button 1 pressed. Enter Idle mode DMA_EnableChannel( &DMA.CH0 ); // Set and enable sleep. SLEEP.CTRL = (SLEEP.CTRL & ~SLEEP_SMODE_gm) | SLEEP_SMODE_IDLE_gc; SLEEP.CTRL |= SLEEP_SEN_bm; // Enable sleep. } else if (SLEEP.CTRL & SLEEP_SEN_bm) { // We are in wanting-to-sleep mode, but were awake. sleep(); } else { // Do active sampling and transfer of ADC data. LEDPORT.OUT = ADCB.CH0RES & 0xFF; } } }