В апноуте AVR319 все расписано.
даже код есть. под IAR.
вот драйвер:
- Код: Выделить всё • Развернуть
// This file has been prepared for Doxygen automatic documentation generation.
/*! \file ********************************************************************
*
* Atmel Corporation
*
* \li File: spi_via_usi_driver.c
* \li Compiler: IAR EWAAVR 3.10c
* \li Support mail: avr@atmel.com
*
* \li Supported devices: All devices with Universal Serial Interface (USI)
* capabilities can be used.
* The example is written for ATmega169.
*
* \li AppNote: AVR319 - Using the USI module for SPI communication.
*
* \li Description: Example on how to use the USI module for communicating
* with SPI compatible devices. The functions and variables
* prefixed 'spiX_' can be renamed to be able to use several
* spi drivers (using different interfaces) with similar names.
* Some basic SPI knowledge is assumed.
*
* $Revision: 1.4 $
* $Date: Monday, September 13, 2004 12:08:54 UTC $
****************************************************************************/
#include <ioavr.h>
#include <inavr.h>
/* USI port and pin definitions.
*/
#define USI_OUT_REG PORTE //!< USI port output register.
#define USI_IN_REG PINE //!< USI port input register.
#define USI_DIR_REG DDRE //!< USI port direction register.
#define USI_CLOCK_PIN PE4 //!< USI clock I/O pin.
#define USI_DATAIN_PIN PE5 //!< USI data input pin.
#define USI_DATAOUT_PIN PE6 //!< USI data output pin.
/* Speed configuration:
* Bits per second = CPUSPEED / PRESCALER / (COMPAREVALUE+1) / 2.
* Maximum = CPUSPEED / 64.
*/
#define TC0_PRESCALER_VALUE 256 //!< Must be 1, 8, 64, 256 or 1024.
#define TC0_COMPARE_VALUE 1 //!< Must be 0 to 255. Minimum 31 with prescaler CLK/1.
/* Prescaler value converted to bit settings.
*/
#if TC0_PRESCALER_VALUE == 1
#define TC0_PS_SETTING (1<<CS00)
#elif TC0_PRESCALER_VALUE == 8
#define TC0_PS_SETTING (1<<CS01)
#elif TC0_PRESCALER_VALUE == 64
#define TC0_PS_SETTING (1<<CS01)|(1<<CS00)
#elif TC0_PRESCALER_VALUE == 256
#define TC0_PS_SETTING (1<<CS02)
#elif TC0_PRESCALER_VALUE == 1024
#define TC0_PS_SETTING (1<<CS02)|(1<<CS00)
#else
#error Invalid T/C0 prescaler setting.
#endif
/*! \brief Data input register buffer.
*
* Incoming bytes are stored in this byte until the next transfer is complete.
* This byte can be used the same way as the SPI data register in the native
* SPI module, which means that the byte must be read before the next transfer
* completes and overwrites the current value.
*/
unsigned char storedUSIDR;
/*! \brief Driver status bit structure.
*
* This struct contains status flags for the driver.
* The flags have the same meaning as the corresponding status flags
* for the native SPI module. The flags should not be changed by the user.
* The driver takes care of updating the flags when required.
*/
struct usidriverStatus_t {
unsigned char masterMode : 1; //!< True if in master mode.
unsigned char transferComplete : 1; //!< True when transfer completed.
unsigned char writeCollision : 1; //!< True if put attempted during transfer.
};
volatile struct usidriverStatus_t spiX_status; //!< The driver status bits.
/*! \brief Timer/Counter 0 Compare Match Interrupt handler.
*
* This interrupt handler is only enabled when transferring data
* in master mode. It toggles the USI clock pin, i.e. two interrupts
* results in one clock period on the clock pin and for the USI counter.
*/
#pragma vector=TIMER0_COMP_vect
__interrupt void timer0comp_handler()
{
USICR |= (1<<USITC); // Toggle clock output pin.
}
/*! \brief USI Timer Overflow Interrupt handler.
*
* This handler disables the compare match interrupt if in master mode.
* When the USI counter overflows, a byte has been transferred, and we
* have to stop the timer tick.
* For all modes the USIDR contents are stored and flags are updated.
*/
#pragma vector=USI_OVF_vect
__interrupt void usiovf_handler()
{
// Master must now disable the compare match interrupt
// to prevent more USI counter clocks.
if( spiX_status.masterMode == 1 ) {
TIMSK0 &= ~(1<<OCIE0A);
}
// Update flags and clear USI counter
USISR = (1<<USIOIF);
spiX_status.transferComplete = 1;
// Copy USIDR to buffer to prevent overwrite on next transfer.
storedUSIDR = USIDR;
}
/*! \brief Initialize USI as SPI master.
*
* This function sets up all pin directions and module configurations.
* Use this function initially or when changing from slave to master mode.
* Note that the stored USIDR value is cleared.
*
* \param spi_mode Required SPI mode, must be 0 or 1.
*/
void spiX_initmaster( char spi_mode )
{
// Configure port directions.
USI_DIR_REG |= (1<<USI_DATAOUT_PIN) | (1<<USI_CLOCK_PIN); // Outputs.
USI_DIR_REG &= ~(1<<USI_DATAIN_PIN); // Inputs.
USI_OUT_REG |= (1<<USI_DATAIN_PIN); // Pull-ups.
// Configure USI to 3-wire master mode with overflow interrupt.
USICR = (1<<USIOIE) | (1<<USIWM0) |
(1<<USICS1) | (spi_mode<<USICS0) |
(1<<USICLK);
// Enable 'Clear Timer on Compare match' and init prescaler.
TCCR0A = (1<<WGM01) | TC0_PS_SETTING;
// Init Output Compare Register.
OCR0A = TC0_COMPARE_VALUE;
// Init driver status register.
spiX_status.masterMode = 1;
spiX_status.transferComplete = 0;
spiX_status.writeCollision = 0;
storedUSIDR = 0;
}
/*! \brief Initialize USI as SPI slave.
*
* This function sets up all pin directions and module configurations.
* Use this function initially or when changing from master to slave mode.
* Note that the stored USIDR value is cleared.
*
* \param spi_mode Required SPI mode, must be 0 or 1.
*/
void spiX_initslave( char spi_mode )
{
// Configure port directions.
USI_DIR_REG |= (1<<USI_DATAOUT_PIN); // Outputs.
USI_DIR_REG &= ~(1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN); // Inputs.
USI_OUT_REG |= (1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN); // Pull-ups.
// Configure USI to 3-wire slave mode with overflow interrupt.
USICR = (1<<USIOIE) | (1<<USIWM0) |
(1<<USICS1) | (spi_mode<<USICS0);
// Init driver status register.
spiX_status.masterMode = 0;
spiX_status.transferComplete = 0;
spiX_status.writeCollision = 0;
storedUSIDR = 0;
}
/*! \brief Put one byte on bus.
*
* Use this function like you would write to the SPDR register in the native SPI module.
* Calling this function in master mode starts a transfer, while in slave mode, a
* byte will be prepared for the next transfer initiated by the master device.
* If a transfer is in progress, this function will set the write collision flag
* and return without altering the data registers.
*
* \returns 0 if a write collision occurred, 1 otherwise.
*/
char spiX_put( unsigned char val )
{
// Check if transmission in progress,
// i.e. USI counter unequal to zero.
if( (USISR & 0x0F) != 0 ) {
// Indicate write collision and return.
spiX_status.writeCollision = 1;
return;
}
// Reinit flags.
spiX_status.transferComplete = 0;
spiX_status.writeCollision = 0;
// Put data in USI data register.
USIDR = val;
// Master should now enable compare match interrupts.
if( spiX_status.masterMode == 1 ) {
TIFR0 |= (1<<OCF0A); // Clear compare match flag.
TIMSK0 |= (1<<OCIE0A); // Enable compare match interrupt.
}
if( spiX_status.writeCollision == 0 ) return 1;
return 0;
}
/*! \brief Get one byte from bus.
*
* This function only returns the previous stored USIDR value.
* The transfer complete flag is not checked. Use this function
* like you would read from the SPDR register in the native SPI module.
*/
unsigned char spiX_get()
{
return storedUSIDR;
}
/*! \brief Wait for transfer to complete.
*
* This function waits until the transfer complete flag is set.
* Use this function like you would wait for the native SPI interrupt flag.
*/
void spiX_wait()
{
do {} while( spiX_status.transferComplete == 0 );
}
// end of file
вот пример использования:
- Код: Выделить всё • Развернуть
/* Test application for the SPI-via-USI-driver. */
#include "spi_via_usi_driver.c"
#define SPIMODE 0 // Sample on leading _rising_ edge, setup on trailing _falling_ edge.
//#define SPIMODE 1 // Sample on leading _falling_ edge, setup on trailing _rising_ edge.
void main()
{
unsigned char val = 0; // Temp value to send.
DDRB = 0xFF; // Set PORTB to all output.
// spiX_initmaster(SPIMODE); // Init SPI driver as master.
spiX_initslave(SPIMODE); // Init SPI driver as slave.
__enable_interrupt(); // Must do this to make driver work.
do {
spiX_put( val++ ); // Send temp value to SPI and increment,
spiX_wait(); // wait for transmission to finish
PORTB = spiX_get(); // and finally put result on PORTB.
} while(1); // Loop forever...
}
Мне хотелось бы это дело переписать под CodeVision.
Вот что у меня получается:
- Код: Выделить всё • Развернуть
#include <tiny2313.h>
// Declare your global variables here
void Init(void){
// Declare your local variables here
// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
// Input/Output Ports initialization
// Port A initialization
// Func2=In Func1=In Func0=In
// State2=T State1=T State0=T
PORTA=0x00;
DDRA=0x00;
// Port B initialization
// Func7=In Func6=Out Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=0 State5=T State4=T State3=T State2=T State1=T State0=T
PORTB=0x00;
DDRB=0xC0;
// Port D initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=0x80;
TCCR0B=0x01;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// Interrupt on any change on pins PCINT0-7: Off
GIMSK=0x00;
MCUCR=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x03;
// Universal Serial Interface initialization
// Mode: Three Wire (SPI)
// Clock source: Reg.=ext. pos. edge, Cnt.=USITC
// USI Counter Overflow Interrupt: Off
//USICR=0x23;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
}
/* USI port and pin definitions.
*/
#define USI_OUT_REG PORTB //!< USI port output register.
#define USI_IN_REG PINB //!< USI port input register.
#define USI_DIR_REG DDRB //!< USI port direction register.
#define USI_CLOCK_PIN PORTB.7 //!< USI clock I/O pin.
#define USI_DATAIN_PIN PORTB.5 //!< USI data input pin.
#define USI_DATAOUT_PIN PORTB.6 //!< USI data output pin.
/* Speed configuration:
* Bits per second = CPUSPEED / PRESCALER / (COMPAREVALUE+1) / 2.
* Maximum = CPUSPEED / 64.
*/
#define TC0_PRESCALER_VALUE 256 //!< Must be 1, 8, 64, 256 or 1024.
#define TC0_COMPARE_VALUE 1 //!< Must be 0 to 255. Minimum 31 with prescaler CLK/1.
/* Prescaler value converted to bit settings.
*/
#if TC0_PRESCALER_VALUE == 1
#define TC0_PS_SETTING (TCCR0B+1)
#elif TC0_PRESCALER_VALUE == 8
#define TC0_PS_SETTING (TCCR0B+2)
#elif TC0_PRESCALER_VALUE == 64
#define TC0_PS_SETTING (TCCR0B+2)|(TCCR0B+1)
#elif TC0_PRESCALER_VALUE == 256
#define TC0_PS_SETTING (TCCR0B+4)
#elif TC0_PRESCALER_VALUE == 1024
#define TC0_PS_SETTING (TCCR0B+4)|(TCCR0B+1)
#else
#error Invalid T/C0 prescaler setting.
#endif
/*! \brief Data input register buffer.
*
* Incoming bytes are stored in this byte until the next transfer is complete.
* This byte can be used the same way as the SPI data register in the native
* SPI module, which means that the byte must be read before the next transfer
* completes and overwrites the current value.
*/
unsigned char storedUSIDR;
/*! \brief Driver status bit structure.
*
* This struct contains status flags for the driver.
* The flags have the same meaning as the corresponding status flags
* for the native SPI module. The flags should not be changed by the user.
* The driver takes care of updating the flags when required.
*/
struct usidriverStatus_t {
unsigned char masterMode : 1; //!< True if in master mode.
unsigned char transferComplete : 1; //!< True when transfer completed.
unsigned char writeCollision : 1; //!< True if put attempted during transfer.
};
volatile struct usidriverStatus_t spiX_status; //!< The driver status bits.
/*! \brief Timer/Counter 0 Compare Match Interrupt handler.
*
* This interrupt handler is only enabled when transferring data
* in master mode. It toggles the USI clock pin, i.e. two interrupts
* results in one clock period on the clock pin and for the USI counter.
*/
interrupt [TIM0_COMPA] void timer0_compa_isr(void)
{
// Place your code here
USICR |= (1<<USICR.0); // Toggle clock output pin.
}
/*! \brief USI Timer Overflow Interrupt handler.
*
* This handler disables the compare match interrupt if in master mode.
* When the USI counter overflows, a byte has been transferred, and we
* have to stop the timer tick.
* For all modes the USIDR contents are stored and flags are updated.
*/
interrupt [USI_OVERFLOW] void usiovf_handler()
{
// Master must now disable the compare match interrupt
// to prevent more USI counter clocks.
if( spiX_status.masterMode == 1 ) {
TIMSK &= ~(TIMSK+1);
}
// Update flags and clear USI counter
USISR = (1<<USISR.6);
spiX_status.transferComplete = 1;
// Copy USIDR to buffer to prevent overwrite on next transfer.
storedUSIDR = USIDR;
}
/*! \brief Initialize USI as SPI master.
*
* This function sets up all pin directions and module configurations.
* Use this function initially or when changing from slave to master mode.
* Note that the stored USIDR value is cleared.
*
* \param spi_mode Required SPI mode, must be 0 or 1.
*/
void spiX_initmaster( char spi_mode )
{
// Configure port directions.
USI_DIR_REG |= (1<<USI_DATAOUT_PIN) | (1<<USI_CLOCK_PIN); // Outputs.
USI_DIR_REG &= ~(1<<USI_DATAIN_PIN); // Inputs.
USI_OUT_REG |= (1<<USI_DATAIN_PIN); // Pull-ups.
// Configure USI to 3-wire master mode with overflow interrupt.
USICR = (1<<USICR.6) | (1<<USICR.4) |
(1<<USICR.3) | (spi_mode<<USICR.2) |
(1<<USISR.1);
// Enable 'Clear Timer on Compare match' and init prescaler.
TCCR0A = TCCR0A+2 | TC0_PS_SETTING;
// Init Output Compare Register.
OCR0A = TC0_COMPARE_VALUE;
// Init driver status register.
spiX_status.masterMode = 1;
spiX_status.transferComplete = 0;
spiX_status.writeCollision = 0;
storedUSIDR = 0;
}
/*! \brief Initialize USI as SPI slave.
*
* This function sets up all pin directions and module configurations.
* Use this function initially or when changing from master to slave mode.
* Note that the stored USIDR value is cleared.
*
* \param spi_mode Required SPI mode, must be 0 or 1.
*/
void spiX_initslave( char spi_mode )
{
// Configure port directions.
USI_DIR_REG |= (1<<USI_DATAOUT_PIN); // Outputs.
USI_DIR_REG &= ~(1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN); // Inputs.
USI_OUT_REG |= (1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN); // Pull-ups.
// Configure USI to 3-wire slave mode with overflow interrupt.
USICR = (1<<USISR.6) | (1<<USICR.4) |
(1<<USICR.3) | (spi_mode<<USICR.2);
// Init driver status register.
spiX_status.masterMode = 0;
spiX_status.transferComplete = 0;
spiX_status.writeCollision = 0;
storedUSIDR = 0;
}
/*! \brief Put one byte on bus.
*
* Use this function like you would write to the SPDR register in the native SPI module.
* Calling this function in master mode starts a transfer, while in slave mode, a
* byte will be prepared for the next transfer initiated by the master device.
* If a transfer is in progress, this function will set the write collision flag
* and return without altering the data registers.
*
* \returns 0 if a write collision occurred, 1 otherwise.
*/
char spiX_put( unsigned char val )
{
// Check if transmission in progress,
// i.e. USI counter unequal to zero.
if( (USISR & 0x0F) != 0 ) {
// Indicate write collision and return.
spiX_status.writeCollision = 1;
return;
}
// Reinit flags.
spiX_status.transferComplete = 0;
spiX_status.writeCollision = 0;
// Put data in USI data register.
USIDR = val;
// Master should now enable compare match interrupts.
if( spiX_status.masterMode == 1 ) {
TIFR |= TIFR+1; // Clear compare match flag.
TIMSK |= TIMSK+1; // Enable compare match interrupt.
}
if( spiX_status.writeCollision == 0 ) return 1;
return 0;
}
/*! \brief Get one byte from bus.
*
* This function only returns the previous stored USIDR value.
* The transfer complete flag is not checked. Use this function
* like you would read from the SPDR register in the native SPI module.
*/
unsigned char spiX_get()
{
return storedUSIDR;
}
/*! \brief Wait for transfer to complete.
*
* This function waits until the transfer complete flag is set.
* Use this function like you would wait for the native SPI interrupt flag.
*/
void spiX_wait()
{
do {} while( spiX_status.transferComplete == 0 );
}
// end of file
#define SPIMODE 0 // Sample on leading _rising_ edge, setup on trailing _falling_ edge.
//#define SPIMODE 1 // Sample on leading _falling_ edge, setup on trailing _rising_ edge.
void main(void){
unsigned char val = 0;
Init();
spiX_initmaster(SPIMODE); // Init SPI driver as master.
// spiX_initslave(SPIMODE); // Init SPI driver as slave.
while(1){
spiX_put(val++);
spiX_wait();
};
}
естественно не работает.
PORTB.7 все время в единице.
очевидно, я где-то что-то не так понимаю и соответственно пишу неправильно.
кто-нибудь поможет с этим разобраться?