Tuesday, August 16, 2016

CC1101 Atmega32u C LUFA source code


This code is from project: CC1101 Atmega32u USB dongle

cc1101_dongle.c

#include "cc1101_dongle.h"
#include "usb_desc.h"

#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <avr/interrupt.h>


struct spi_reg { 
   uint16_t reg;
   uint16_t val;
};


uint8_t spi_write(uint8_t data) {
   L(TIMSK0, TOIE0);
      SPDR = data;
      while(!(SPSR & (1 << SPIF)));
   H(TIMSK0, TOIE0);
   return SPDR;
}


uint8_t spi_strobe(uint8_t s) {
   uint8_t r = 0;
   ENABLE;
      r = spi_write(s);
   DISABLE;
   return r;
}


void spi_reg_write(struct spi_reg *r) {
   ENABLE;
      spi_write(r->reg | WRITE_BURST);
      spi_write(r->val);
   DISABLE;
}


void spi_reg_read(struct spi_reg *r) {
   ENABLE;
      spi_write(r->reg | READ_BURST);
      r->val = spi_write(0);
   DISABLE;
}


uint8_t spi_reg_read_single(uint8_t a) {
   uint8_t r = 0;
   ENABLE;
      spi_write(a | READ_BURST);
      r = spi_write(0);
   DISABLE;
   return r;
}


USB_ClassInfo_CDC_Device_t serial = {
  .Config = {
    .ControlInterfaceNumber   = INTERFACE_ID_CDC_CCI,
    .DataINEndpoint           = {
      .Address          = CDC_TX_EPADDR,
      .Size             = CDC_TXRX_EPSIZE,
      .Banks            = 1,
     },
    .DataOUTEndpoint = {
      .Address          = CDC_RX_EPADDR,
      .Size             = CDC_TXRX_EPSIZE,
      .Banks            = 1,
     },
    .NotificationEndpoint = {
      .Address          = CDC_NOTIFICATION_EPADDR,
      .Size             = CDC_NOTIFICATION_EPSIZE,
      .Banks            = 1,
     },
   },
 };


void EVENT_USB_Device_Connect(void) { }
void EVENT_USB_Device_Disconnect(void) { }
void EVENT_USB_Device_ConfigurationChanged(void) { CDC_Device_ConfigureEndpoints(&serial); }
void EVENT_USB_Device_ControlRequest(void) { CDC_Device_ProcessControlRequest(&serial); }


uint8_t usb_cfg = 0;
void EVENT_CDC_Device_ControLineStateChanged(USB_ClassInfo_CDC_Device_t *const CDCInterfaceInfo) {
   L(EIMSK, INT2);
 usb_cfg = (CDCInterfaceInfo->State.ControlLineStates.HostToDevice & CDC_CONTROL_LINE_OUT_DTR) != 0;
}


void led_strobe() {
   static strobe = 0;
   if(strobe) {
      strobe = 0;
      LED_ON;
   }
   else {
      LED_OFF;
      strobe = 1;
   }
}


uint8_t packet_len = 0;
uint8_t bytes_to_send = 0;

uint8_t buff[255] = {};
uint8_t buff_idx = 0;

uint8_t timer = 0;


ISR(TIMER0_OVF_vect) {  
   volatile uint8_t i = 0;

   CDC_Device_USBTask(&serial);
   USB_USBTask();

   timer++;

   // blink led periodically
   if (timer > 40) {
      led_strobe();
      timer = 0;
   }
}  


// FIFO above threshold interrupt
ISR(INT2_vect) {
   volatile uint8_t val = spi_reg_read_single(RXBYTES);
   volatile uint8_t i = 0;

   ENABLE;
   spi_write(READ_BURST | RXFIFO);
   for(i = 0; i < (val & 0x7F); i++) {
      buff[buff_idx++] = spi_write(0);
   } 
   DISABLE;
   
   // this is only good for small data rates,
   // probably ring buffer would be better here
   if (buff_idx == packet_len) {
      for(i = 0; i < buff_idx; i++) {   
         CDC_Device_SendByte(&serial, buff[i]);
      }
      buff_idx = 0;
   } 

   CDC_Device_USBTask(&serial);
   USB_USBTask();
}



/****************************************************************************/
/*                                                                          */
/****************************************************************************/
int main()
{
   H(DDRE, PE6);  // led

   L(DDRD, PD2);  // GDO2
   L(DDRD, PD3);  // GDO0

   H(PORTD, PD2);
   H(PORTD, PD3);

   H(DDRB, PB0);  // SS
   H(DDRB, PB1);  // SCK
   H(DDRB, PB2);  // MOSI
   L(DDRB, PB3);  // MISO

   DISABLE;
   H(SPSR, SPI2X);
   H(SPCR, MSTR);
   H(SPCR, SPE);

   MCUSR &= ~(1 << WDRF);
   wdt_disable();
   clock_prescale_set(clock_div_1);
   USB_Init();

   H(TCCR0B, CS00);
   H(TCCR0B, CS02);
   H(TIMSK0, TOIE0);

   H(EICRA, ISC20);
   H(EICRA, ISC21);
   L(EIMSK, INT2);

   GlobalInterruptEnable();

   // reset CC1101
   DISABLE; 
   _delay_us(50);
   ENABLE; 
   spi_strobe(SRES);
   _delay_us(200);
   spi_strobe(SIDLE);
   spi_strobe(SFRX);
   spi_strobe(SFTX);

   int16_t c = 0;
   struct spi_reg r;
   uint8_t r_idx = 0;

 while(1) {
  c = CDC_Device_ReceiveByte(&serial);

      if (c < 0)
         continue;

      // if we have seen DTR before, push config into cc1101
      if (usb_cfg > 0) {

         if (usb_cfg == 1) {
             // first stage is to flush buffs and change cc1101 state
             // to idle
             usb_cfg = 2;

             spi_strobe(SIDLE);
             while(spi_reg_read_single(MARCSTATE) != MARCSTATE_IDLE);
             spi_strobe(SFRX);
             spi_strobe(SFTX);
             buff_idx = 0;
         }

         // if we hit end of config 0xFF, back to normal operation, enable
         // fifo interrupt
         if (c == 0xff && r_idx == 0) {
            usb_cfg = 0;
            spi_strobe(SRX);
            while(spi_reg_read_single(MARCSTATE) != MARCSTATE_RX);
            H(EIMSK, INT2);
            continue;
         }

         if (r_idx == 0) {
            r.reg = c;
            r_idx++;
         } else {
            r.val = c;
            r_idx = 0;

            // in this special case, copy PKTLEN to variable
            if (r.reg == PKTLEN) {
               packet_len = r.val;
               bytes_to_send = packet_len;
            }
            spi_reg_write(&r);
         }

      } else {

         // we've got some data, push it to TX fifo, then
         // push over air
         ENABLE;
            spi_write(TXFIFO);
            spi_write(c);
         DISABLE;

         bytes_to_send--;

         if(bit_is_set(PIND, PD3) && bytes_to_send > 0) {
            spi_strobe(STX);
            while(spi_reg_read_single(MARCSTATE) != MARCSTATE_TXFIFO_UNDERFLOW);
            spi_strobe(SFTX);
         }

         if (!bytes_to_send) {
            spi_strobe(STX);

            if (packet_len <= 64)
               while(spi_reg_read_single(MARCSTATE) != MARCSTATE_IDLE);
            else
               while(spi_reg_read_single(MARCSTATE) != MARCSTATE_TXFIFO_UNDERFLOW);

            if (packet_len <= 64) 
               while(spi_reg_read_single(MARCSTATE) != MARCSTATE_IDLE);

            spi_strobe(SFTX);
            bytes_to_send = packet_len;

            spi_strobe(SRX);
            while(spi_reg_read_single(MARCSTATE) != MARCSTATE_RX);
         }
      }
   } 
}

cc1101_dongle.h

#ifndef _H_CC1101_DONGLE_H_
#define _H_CC1101_DONGLE_H_

#define H(a,b) a |=(1<<(b))
#define L(a,b) a &=~(1<<(b))
#define IS(a,b) bit_is_set(a,b)
#define BS(a,b) (a & (1<<(b)))


#define ENABLE   L(PORTB, PB0)
#define DISABLE  H(PORTB, PB0)

#define LED_ON    H(PORTE, PE6)
#define LED_OFF   L(PORTE, PE6)

// some of CC1101 registers and states
#define TXFIFO      0x3F
#define RXFIFO      0x3F
#define STX         0x35
#define SRX         0x34
#define SFTX        0x3B
#define SFRX        0x3A
#define READ_BURST  0xC0
#define WRITE_BURST 0x40
#define RXBYTES     0x3B
#define TXBYTES     0x3A

#define PKTLEN      0x06

#define MARCSTATE   0x35

#define SIDLE       0x36
#define SRES        0x30


#define MARCSTATE_IDLE             0x01
#define MARCSTATE_RX               0x0D
#define MARCSTATE_TXFIFO_UNDERFLOW 0x16


#endif

#ifndef F_CPU
   #error CPU speed unknown
#endif


usb_desc.c

#include "usb_desc.h"


/** Device descriptor structure. This descriptor, located in FLASH memory, describes the overall
 *  device characteristics, including the supported USB version, control endpoint size and the
 *  number of device configurations. The descriptor is read out by the USB host when the enumeration
 *  process begins.
 */
const USB_Descriptor_Device_t PROGMEM DeviceDescriptor = {
 .Header                 = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device},

 .USBSpecification       = VERSION_BCD(1,1,0),
 .Class                  = CDC_CSCP_CDCClass,
 .SubClass               = CDC_CSCP_NoSpecificSubclass,
 .Protocol               = CDC_CSCP_NoSpecificProtocol,

 .Endpoint0Size          = FIXED_CONTROL_ENDPOINT_SIZE,

 .VendorID               = 0x03EB,
 .ProductID              = 0x2044,
 .ReleaseNumber          = VERSION_BCD(0,0,1),

 .ManufacturerStrIndex   = STRING_ID_Manufacturer,
 .ProductStrIndex        = STRING_ID_Product,
 .SerialNumStrIndex      = USE_INTERNAL_SERIAL,

 .NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS
};

/** Configuration descriptor structure. This descriptor, located in FLASH memory, describes the usage
 *  of the device in one of its supported configurations, including information about any device interfaces
 *  and endpoints. The descriptor is read out by the USB host during the enumeration process when selecting
 *  a configuration so that the host may correctly communicate with the USB device.
 */
const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = {
 .Config = {
   .Header                 = {.Size = sizeof(USB_Descriptor_Configuration_Header_t), .Type = DTYPE_Configuration},

   .TotalConfigurationSize = sizeof(USB_Descriptor_Configuration_t),
   .TotalInterfaces        = 2,

   .ConfigurationNumber    = 1,
   .ConfigurationStrIndex  = NO_DESCRIPTOR,

   .ConfigAttributes       = (USB_CONFIG_ATTR_RESERVED | USB_CONFIG_ATTR_SELFPOWERED),

   .MaxPowerConsumption    = USB_CONFIG_POWER_MA(100)
  },

 .CDC_CCI_Interface = {
   .Header                 = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface},

   .InterfaceNumber        = INTERFACE_ID_CDC_CCI,
   .AlternateSetting       = 0,

   .TotalEndpoints         = 1,

   .Class                  = CDC_CSCP_CDCClass,
   .SubClass               = CDC_CSCP_ACMSubclass,
   .Protocol               = CDC_CSCP_ATCommandProtocol,

   .InterfaceStrIndex      = NO_DESCRIPTOR
  },

 .CDC_Functional_Header = {
   .Header                 = {.Size = sizeof(USB_CDC_Descriptor_FunctionalHeader_t), .Type = DTYPE_CSInterface},
   .Subtype                = CDC_DSUBTYPE_CSInterface_Header,

   .CDCSpecification       = VERSION_BCD(1,1,0),
  },

 .CDC_Functional_ACM = {
   .Header                 = {.Size = sizeof(USB_CDC_Descriptor_FunctionalACM_t), .Type = DTYPE_CSInterface},
   .Subtype                = CDC_DSUBTYPE_CSInterface_ACM,

   .Capabilities           = 0x06,
  },

 .CDC_Functional_Union = {
   .Header                 = {.Size = sizeof(USB_CDC_Descriptor_FunctionalUnion_t), .Type = DTYPE_CSInterface},
   .Subtype                = CDC_DSUBTYPE_CSInterface_Union,

   .MasterInterfaceNumber  = INTERFACE_ID_CDC_CCI,
   .SlaveInterfaceNumber   = INTERFACE_ID_CDC_DCI,
  },

 .CDC_NotificationEndpoint = {
   .Header                 = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},

   .EndpointAddress        = CDC_NOTIFICATION_EPADDR,
   .Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
   .EndpointSize           = CDC_NOTIFICATION_EPSIZE,
   .PollingIntervalMS      = 0xFF
  },

 .CDC_DCI_Interface = {
   .Header                 = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface},

   .InterfaceNumber        = INTERFACE_ID_CDC_DCI,
   .AlternateSetting       = 0,

   .TotalEndpoints         = 2,

   .Class                  = CDC_CSCP_CDCDataClass,
   .SubClass               = CDC_CSCP_NoDataSubclass,
   .Protocol               = CDC_CSCP_NoDataProtocol,

   .InterfaceStrIndex      = NO_DESCRIPTOR
  },

 .CDC_DataOutEndpoint = {
   .Header                 = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},

   .EndpointAddress        = CDC_RX_EPADDR,
   .Attributes             = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
   .EndpointSize           = CDC_TXRX_EPSIZE,
   .PollingIntervalMS      = 0x05
  },

 .CDC_DataInEndpoint = {
   .Header                 = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},

   .EndpointAddress        = CDC_TX_EPADDR,
   .Attributes             = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
   .EndpointSize           = CDC_TXRX_EPSIZE,
   .PollingIntervalMS      = 0x05
  }
};

/** Language descriptor structure. This descriptor, located in FLASH memory, is returned when the host requests
 *  the string descriptor with index 0 (the first index). It is actually an array of 16-bit integers, which indicate
 *  via the language ID table available at USB.org what languages the device supports for its string descriptors.
 */
const USB_Descriptor_String_t PROGMEM LanguageString = USB_STRING_DESCRIPTOR_ARRAY(LANGUAGE_ID_ENG);

/** Manufacturer descriptor string. This is a Unicode string containing the manufacturer's details in human readable
 *  form, and is read out upon request by the host when the appropriate string ID is requested, listed in the Device
 *  Descriptor.
 */
const USB_Descriptor_String_t PROGMEM ManufacturerString = USB_STRING_DESCRIPTOR(L"cc1101_test");

/** Product descriptor string. This is a Unicode string containing the product's details in human readable form,
 *  and is read out upon request by the host when the appropriate string ID is requested, listed in the Device
 *  Descriptor.
 */
const USB_Descriptor_String_t PROGMEM ProductString = USB_STRING_DESCRIPTOR(L"cc1101_test");

/** This function is called by the library when in device mode, and must be overridden (see library "USB Descriptors"
 *  documentation) by the application code so that the address and size of a requested descriptor can be given
 *  to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function
 *  is called so that the descriptor details can be passed back and the appropriate descriptor sent back to the
 *  USB host.
 */
uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
                                    const uint8_t wIndex,
                                    const void** const DescriptorAddress) {
 const uint8_t  DescriptorType   = (wValue >> 8);
 const uint8_t  DescriptorNumber = (wValue & 0xFF);

 const void* Address = NULL;
 uint16_t    Size    = NO_DESCRIPTOR;

 switch (DescriptorType) {
  case DTYPE_Device:
   Address = &DeviceDescriptor;
   Size    = sizeof(USB_Descriptor_Device_t);
   break;
  case DTYPE_Configuration:
   Address = &ConfigurationDescriptor;
   Size    = sizeof(USB_Descriptor_Configuration_t);
   break;
  case DTYPE_String:
   switch (DescriptorNumber) {
    case STRING_ID_Language:
     Address = &LanguageString;
     Size    = pgm_read_byte(&LanguageString.Header.Size);
     break;
    case STRING_ID_Manufacturer:
     Address = &ManufacturerString;
     Size    = pgm_read_byte(&ManufacturerString.Header.Size);
     break;
    case STRING_ID_Product:
     Address = &ProductString;
     Size    = pgm_read_byte(&ProductString.Header.Size);
     break;
   }

   break;
 }

 *DescriptorAddress = Address;
 return Size;
}

usb_desc.h

#ifndef _USB_DESC_H_
#define _USB_DESC_H_

#include "cc1101_dongle.h"
#include "../../LUFA/Drivers/USB/USB.h"

/* Macros: */
/** Endpoint address of the CDC device-to-host notification IN endpoint. */
#define CDC_NOTIFICATION_EPADDR        (ENDPOINT_DIR_IN  | 2)

/** Endpoint address of the CDC device-to-host data IN endpoint. */
#define CDC_TX_EPADDR                  (ENDPOINT_DIR_IN  | 3)

/** Endpoint address of the CDC host-to-device data OUT endpoint. */
#define CDC_RX_EPADDR                  (ENDPOINT_DIR_OUT | 4)

/** Size in bytes of the CDC device-to-host notification IN endpoint. */
#define CDC_NOTIFICATION_EPSIZE        8

/** Size in bytes of the CDC data IN and OUT endpoints. */
#define CDC_TXRX_EPSIZE                16

/* Type Defines: */
/** Type define for the device configuration descriptor structure. This must be defined in the
 *  application code, as the configuration descriptor contains several sub-descriptors which
 *  vary between devices, and which describe the device's usage to the host.
 */
typedef struct {
   USB_Descriptor_Configuration_Header_t    Config;

   // CDC Control Interface
   USB_Descriptor_Interface_t               CDC_CCI_Interface;
   USB_CDC_Descriptor_FunctionalHeader_t    CDC_Functional_Header;
   USB_CDC_Descriptor_FunctionalACM_t       CDC_Functional_ACM;
   USB_CDC_Descriptor_FunctionalUnion_t     CDC_Functional_Union;
   USB_Descriptor_Endpoint_t                CDC_NotificationEndpoint;

   // CDC Data Interface
   USB_Descriptor_Interface_t               CDC_DCI_Interface;
   USB_Descriptor_Endpoint_t                CDC_DataOutEndpoint;
   USB_Descriptor_Endpoint_t                CDC_DataInEndpoint;
} USB_Descriptor_Configuration_t;

/** Enum for the device interface descriptor IDs within the device. Each interface descriptor
 *  should have a unique ID index associated with it, which can be used to refer to the
 *  interface from other descriptors.
 */
enum InterfaceDescriptors_t {
   INTERFACE_ID_CDC_CCI = 0, /**< CDC CCI interface descriptor ID */
   INTERFACE_ID_CDC_DCI = 1, /**< CDC DCI interface descriptor ID */
};

/** Enum for the device string descriptor IDs within the device. Each string descriptor should
 *  have a unique ID index associated with it, which can be used to refer to the string from
 *  other descriptors.
 */
enum StringDescriptors_t {
   STRING_ID_Language     = 0, /**< Supported Languages string descriptor ID (must be zero) */
   STRING_ID_Manufacturer = 1, /**< Manufacturer string ID */
   STRING_ID_Product      = 2, /**< Product string ID */
};

uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
                                    const uint8_t wIndex,
                                    const void** const DescriptorAddress)
                                    ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3);

#endif

makefile

#
#             LUFA Library
#     Copyright (C) Dean Camera, 2013.
#
#  dean [at] fourwalledcubicle [dot] com
#           www.lufa-lib.org
#
# --------------------------------------
#         LUFA Project Makefile.
# --------------------------------------

# Run "make help" for target help.

MCU          = atmega32u4
ARCH         = AVR8
BOARD        = CC1101_TEST
F_CPU        = 8000000
F_USB        = $(F_CPU)
OPTIMIZATION = s
TARGET       = cc1101_dongle
SRC          = $(TARGET).c usb_desc.c $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)

LUFA_PATH    = ../../LUFA
CC_FLAGS     = -DUSE_LUFA_CONFIG_HEADER -IConfig

PROG=dfu-programmer
PROG_FLAGS=$(MCU)

# Default target
all:

upload: 
  $(MAKE) all
    $(MAKE) $(HEX_FILE)
  $(PROG) $(PROG_FLAGS) erase
  $(PROG) $(PROG_FLAGS) flash $(TARGET).hex
  $(PROG) $(PROG_FLAGS) start

# Include LUFA build script makefiles
include $(LUFA_PATH)/Build/lufa_core.mk
include $(LUFA_PATH)/Build/lufa_sources.mk
include $(LUFA_PATH)/Build/lufa_build.mk
include $(LUFA_PATH)/Build/lufa_cppcheck.mk
include $(LUFA_PATH)/Build/lufa_doxygen.mk
include $(LUFA_PATH)/Build/lufa_dfu.mk
include $(LUFA_PATH)/Build/lufa_hid.mk
include $(LUFA_PATH)/Build/lufa_avrdude.mk
include $(LUFA_PATH)/Build/lufa_atprogram.mk

No comments:

Post a Comment