Source file: /~heha/Mikrocontroller/LEDs/u-wire.zip/main.c

/* 
 * µ-wire
 * cpldcpu Jan 2014
 * 
 * Smallest possible USB compliant device based on V-USB.
 * Drives a single WS2812 LED using the little-wire protocol.
 *
 * Copyright (C) 2014 T. Böscke; GNU GPL3
 * --------------------------------------------------------------------------

Works only on ATtiny 9+10 (ATtiny9 requires #define __AVR_ATtiny10__)

Connections: 
PB0	1	USB D- and resistor 10 kΩ from USB 5P
GND	2	GND
PB1	3	USB D+
PB2	4	Data output for WS2812 RGB LED
Ucc	5	From USB 5P via voltage dropping red LED, capacitor 47 nF to GND
PB3	6	(unused, RESET)

Old Connections: 
PB0:  Data output for WS2812 LED
PB1:  USB D-
PB2:  USB D+
 */
 
// WS2812 Pin
 
#define ws2812_mask _BV(PB2)
#define ws2812_port PORTB		// Data port register

#include <avr/io.h>
#include <avr/pgmspace.h>
// Save signature into ELF file for the programmer
#include <avr/signature.h>
// Save Configuration word into ELF file for the programmer
unsigned __attribute((__section__(".config"))) Config=0xFE;
#include <util/delay.h>

#define usbMsgPtr_t uint8_t

#include "usbdrv/usbdrv.c"

// Definition of sei and cli without memory barrier keyword to prevent reloading of memory variables
#define sei() asm volatile("sei")
#define cli() asm volatile("cli")
#define nop() asm volatile("nop")

// Use the old delay routines without NOP padding. This saves memory.
#define __DELAY_BACKWARD_COMPATIBLE__  

/* ------------------------------------------------------------------------ */

static  void  ws2812_sendarray_mask(void) {
 uint8_t curbyte=0,ctr,masklo,maskhi=ws2812_mask;
 masklo=~maskhi;
 uint8_t datlen=3;
 uint8_t *data=usbRxBuf + 4;

 asm volatile(
  "	in	%0,%2	\n"
  "	or	%3,%0	\n"
  "	and	%4,%0	\n"
  "1:	subi	%1,1	\n"	// 12
  "	brcs	3f	\n"	// 14
  "	ld	%6,z+	\n"	// 15
  "	ldi	%0,8	\n"	// 16
  "2:	out	%2,%3   \n"	// 1
  "	rjmp	.+0	\n"	// 3
  "	sbrs	%6,7	\n"	// 4nt / 5t
  "	out	%2,%4	\n"	// 5
  "	dec	%0	\n"	// 6
  "	rjmp	.+0	\n"	// 8	
  "	out	%2,%4	\n"	// 9
  "	breq	1b	\n"	// 10nt  / 11t
  "	lsl	%6	\n"	// 11
  "	rjmp	.+0	\n"	// 13
  "	rjmp	2b	\n"	// 15
  "3:			\n"
  :"=&d" (ctr)
  :"r" (datlen), "I" (_SFR_IO_ADDR(ws2812_port)), "r" (maskhi), "r" (masklo), "z" (data), "r" (curbyte)
 );

}

/* We use if() instead of #if in the macro below because #if can't be used
 * in macros and the compiler optimizes constant conditions anyway.
 * This may cause problems with undefined symbols if compiled without
 * optimizing!
 */
#define GET_DESCRIPTOR(cfgProp, staticName) \
  if(cfgProp){                              \
    len = USB_PROP_LENGTH(cfgProp);         \
    usbMsgPtr = (usbMsgPtr_t)(staticName);  \
  }


#define STS(mem,in) \
    asm volatile ("sts %0,%1"  : : "i" (&mem), "r" (in) :  "memory" );

#ifdef __AVR_ATtiny10__
#define STS_Zero(mem) \
    asm volatile ("sts %0,R17"  : : "i" (&mem) :  "memory" );
#else
#define STS_Zero(mem) \
    asm volatile ("sts %0,R1"  : : "i" (&mem) :  "memory" );
#endif
    
#define LDS(out,mem) \
    asm volatile ("lds %0,%1"  : "=d" (out) : "i" (&mem));
  
/* ------------------------------------------------------------------------ */
void USB_INTR_VECTOR(void); 
int main(void) __attribute__((__noreturn__));
int main(void) {
 usbMsgLen_t  usbMsgLen; /* remaining number of bytes */
 usbMsgPtr_t  usbMsgPtr;  

#ifdef __AVR_ATtiny10__
 CCP=0xD8;   // configuration change protection, write signature
 CLKPSR=0;   // set cpu clock prescaler =1 (8Mhz) (attiny 4/5/9/10)
#endif    
 // usbDeviceDisconnect();  /* do this while interrupts are disabled */
 // _delay_ms(500);  
 usbDeviceConnect();

 usbTxLen = USBPID_NAK;
 usbMsgLen = USB_NO_MSG;
 USB_INTR_CFG |= USB_INTR_CFG_SET;
 USB_INTR_ENABLE |= (1 << USB_INTR_ENABLE_BIT);
    
 DDRB|=ws2812_mask;
 
 calibrateOscillatorASM();
 USB_INTR_PENDING = 1<<USB_INTR_PENDING_BIT;                   
 do { 
  while ( !(USB_INTR_PENDING & (1<<USB_INTR_PENDING_BIT)) );
 //   PORTB|=ws2812_mask;  
  USB_INTR_VECTOR();      
 //   PORTB&=~ws2812_mask;
    
  schar len;
  uchar usbLineStatus;
    
  len = usbRxLen - 3;
  if (len>=0) {
   uint8_t *data=(uint8_t *)usbRxBuf + 1 ;
   usbRequest_t    *rq = (void *)data;   
      
      /* usbRxToken can be:
       * 0x2d 00101101 (USBPID_SETUP for setup data)
       * 0xe1 11100001 (USBPID_OUT: data phase of setup transfer)
       * 0...0x0f for OUT on endpoint X  <- we do not use this
       * We assume that all packets are setup packets. (usbRxToken == USBPID_SETUP)
       * we also assume that there are no malformed requests - no error handling.
       */
     
   usbMsgLen_t replyLen;
//        usbTxBuf[0] = USBPID_DATA0;         /* initialize data toggling */
   STS(usbTxBuf[0], USBPID_DATA0);         /* initialize data toggling */
    //  usbTxLen = USBPID_NAK;              /* abort pending transmit */
   uint8_t   type;     LDS(type,    rq->bmRequestType);
   uint8_t   request;  LDS(request, rq->bRequest);
        
   if ((type & USBRQ_TYPE_MASK) != USBRQ_TYPE_STANDARD){  // All nonstandard setup-requests are updating the LED
    if (request == 34) { // little-wire version reply
     usbMsgPtr = (usbMsgPtr_t)(&usbDescriptorDevice[12]); // Version from usb descriptor
     replyLen=1;          
    }else{
     ws2812_sendarray_mask();
     replyLen=0;
    }
   }else{   // standard requests are handled by driver 
    usbMsgLen_t len = 0;
/*    
          uint8_t   request= rq->bRequest;
          uint8_t   value0 = rq->wValue.bytes[0];
          uint8_t   value1 = rq->wValue.bytes[1];
  */        
    uint8_t value0;     LDS(value0,rq->wValue.bytes[0]); 
    uint8_t value1;     LDS(value1,rq->wValue.bytes[1]); 
    SWITCH_START(request)
     SWITCH_CASE(USBRQ_GET_STATUS)           /* 0 */
                  len = 2;
     SWITCH_CASE(USBRQ_SET_ADDRESS)          /* 5 */
//                  usbNewDeviceAddr = value0;
                  STS(usbNewDeviceAddr,value0);
     SWITCH_CASE(USBRQ_GET_DESCRIPTOR)       /* 6 */
      SWITCH_START(value1)
       SWITCH_CASE(USBDESCR_DEVICE)    /* 1 */
        GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_DEVICE, usbDescriptorDevice)
       SWITCH_CASE(USBDESCR_CONFIG)    /* 2 */
        GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_CONFIGURATION, usbDescriptorConfiguration)
       SWITCH_CASE(USBDESCR_STRING)    /* 3 */
        SWITCH_START(value0)
         SWITCH_CASE(0)
          GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_0, usbDescriptorString0)
         SWITCH_CASE(1)
          GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_VENDOR, usbDescriptorStringVendor)
         SWITCH_CASE(2)
          GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_PRODUCT, usbDescriptorStringDevice)
         SWITCH_CASE(3)
          GET_DESCRIPTOR(USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER, usbDescriptorStringSerialNumber)
         SWITCH_DEFAULT
        SWITCH_END
       SWITCH_DEFAULT
      SWITCH_END
     //         SWITCH_CASE(USBRQ_GET_INTERFACE)        /* 10 */  We do not support an alternative interface
     //             len = 1;
     SWITCH_DEFAULT                          /* 7=SET_DESCRIPTOR, 12=SYNC_FRAME */
    SWITCH_END
    replyLen=len;

    uint8_t requestLen; LDS(requestLen,rq->wLength.bytes[0]);          
    if (replyLen>requestLen) replyLen = requestLen;
   }
   usbMsgLen = replyLen;
        //usbRxLen = 0;       /* mark rx buffer as available */
   STS_Zero(usbRxLen);
  }    
 //   if(usbTxLen & 0x10)   // transmit system is always idle in polled mode
  { usbMsgLen_t wantLen=USBPID_NAK;

   if (usbMsgLen != USB_NO_MSG){    /* transmit data pending? */
    wantLen=usbMsgLen;
    if (wantLen > 8) {
     wantLen = 8;
     usbMsgLen -= wantLen;
    }else{  // end of message reached
     usbMsgLen = USB_NO_MSG;
    }
    {   
     uint8_t i,c;
     usbMsgPtr_t r = usbMsgPtr;             
     uint8_t     *data=usbTxBuf + 1;

        // *data++ ^= USBPID_DATA0 ^ USBPID_DATA1; // DATA toggling 
        //  AVR-GCC 4.7.2 is too stupid to optimize this
#ifdef __AVR_ATtiny10__         
     asm volatile(
	"         ld %0,-Z    \n\t"        
	"         eor %0,%2   \n\t"        
	"         st  Z+,%0  \n\t"   
	: "=&d" (c)
	:  "z" (data), "r" ((uint8_t)(USBPID_DATA0 ^ USBPID_DATA1))
	);            
#else
     asm volatile(
	"         ld %0,-x    \n\t"        
	"         eor %0,%2   \n\t"        
	"         st  x+,%0  \n\t"   
	: "=&d" (c)
	:  "x" (data), "r" ((uint8_t)(USBPID_DATA0 ^ USBPID_DATA1))
	);            
#endif
     i=wantLen;
     while (i--)  // don't bother app with 0 sized reads 
     { 
#ifdef __AVR_ATtiny10__               
//                uint8_t *flashbase=(uint8_t*)0x4000+r;
//               c = *flashbase; 
      asm volatile (" ldi r27,0x40 \n\t ld %0,x"  : "=d" (c) : "x" (r));
#else
      c = USB_READ_FLASH(r);    // assign to char size variable to enforce byte ops 
#endif                
      *data++ = c;
      r++;
     }    
    }
          
    usbMsgPtr += wantLen;            
    usbCrc16Append(&usbTxBuf[1], wantLen);
    wantLen += 4;           /* length including sync byte */
   }
   STS(usbTxLen, wantLen);   
      //usbTxLen = wantLen;
  }    
  if (USB_INTR_PENDING & (1<<USB_INTR_PENDING_BIT))  // Usbpoll() collided with data packet
  {        
   uint8_t ctr;
          // loop takes 5 cycles
   asm volatile(      
          "	ldi	%0,%1	\n"        
          "1:	sbic	%2,%3	\n"        
          "	ldi	%0,%1	\n"
          "	subi	%0,1	\n"        
          "	brne	1b	\n"   
	:"=&d" (ctr)
	: "M" ((uint8_t)(10.0f*(F_CPU/1.0e6f)/5.0f+0.5)), "I" (_SFR_IO_ADDR(USBIN)), "M" (USB_CFG_DPLUS_BIT)
	);       
   USB_INTR_PENDING = 1<<USB_INTR_PENDING_BIT;                   
  }      
 } while(1);  
}
Detected encoding: UTF-80