В апноуте 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 все время в единице.
очевидно, я где-то что-то не так понимаю и соответственно пишу неправильно.
кто-нибудь поможет с этим разобраться?



