📄 main.c
字号:
/* Name: main.c
* Project: AVR USB driver for CDC interface on Low-Speed USB
* Author: Osamu Tamura
* Creation Date: 2006-05-12
* Tabsize: 4
* Copyright: (c) 2006 by Recursion Co., Ltd.
* License: Proprietary, free under certain conditions. See Documentation.
*
* 2006-07-08 removed zero-sized receive block
* 2006-07-08 adapted to higher baud rate by T.Kitazawa
*
*/
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <avr/eeprom.h>
#include <util/delay.h>
#include "usbdrv.h"
#include "oddebug.h"
#include "uart.h"
#if UART_CFG_HAVE_USART
#define HW_CDC_BULK_OUT_SIZE 8
#else
#define HW_CDC_BULK_OUT_SIZE 1
#endif
#define HW_CDC_BULK_IN_SIZE 8
/* Size of bulk transfer packets. The standard demands 8 bytes, but we may
* be better off with less. Try smaller values if the communication hangs.
*/
enum {
SEND_ENCAPSULATED_COMMAND = 0,
GET_ENCAPSULATED_RESPONSE,
SET_COMM_FEATURE,
GET_COMM_FEATURE,
CLEAR_COMM_FEATURE,
SET_LINE_CODING = 0x20,
GET_LINE_CODING,
SET_CONTROL_LINE_STATE,
SEND_BREAK
};
static PROGMEM char configDescrCDC[] = { /* USB configuration descriptor */
9, /* sizeof(usbDescrConfig): length of descriptor in bytes */
USBDESCR_CONFIG, /* descriptor type */
67,
0, /* total length of data returned (including inlined descriptors) */
2, /* number of interfaces in this configuration */
1, /* index of this configuration */
0, /* configuration name string index */
#if USB_CFG_IS_SELF_POWERED
USBATTR_SELFPOWER, /* attributes */
#else
USBATTR_BUSPOWER, /* attributes */
#endif
USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */
/* interface descriptor follows inline: */
9, /* sizeof(usbDescrInterface): length of descriptor in bytes */
USBDESCR_INTERFACE, /* descriptor type */
0, /* index of this interface */
0, /* alternate setting for this interface */
USB_CFG_HAVE_INTRIN_ENDPOINT, /* endpoints excl 0: number of endpoint descriptors to follow */
USB_CFG_INTERFACE_CLASS,
USB_CFG_INTERFACE_SUBCLASS,
USB_CFG_INTERFACE_PROTOCOL,
0, /* string index for interface */
/* CDC Class-Specific descriptor */
5, /* sizeof(usbDescrCDC_HeaderFn): length of descriptor in bytes */
0x24, /* descriptor type */
0, /* header functional descriptor */
0x10, 0x01,
4, /* sizeof(usbDescrCDC_AcmFn): length of descriptor in bytes */
0x24, /* descriptor type */
2, /* abstract control management functional descriptor */
0x02, /* SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE */
5, /* sizeof(usbDescrCDC_UnionFn): length of descriptor in bytes */
0x24, /* descriptor type */
6, /* union functional descriptor */
0, /* CDC_COMM_INTF_ID */
1, /* CDC_DATA_INTF_ID */
5, /* sizeof(usbDescrCDC_CallMgtFn): length of descriptor in bytes */
0x24, /* descriptor type */
1, /* call management functional descriptor */
3, /* allow management on data interface, handles call management by itself */
1, /* CDC_DATA_INTF_ID */
/* Endpoint Descriptor */
7, /* sizeof(usbDescrEndpoint) */
USBDESCR_ENDPOINT, /* descriptor type = endpoint */
0x83, /* IN endpoint number 3 */
0x03, /* attrib: Interrupt endpoint */
8, 0, /* maximum packet size */
USB_CFG_INTR_POLL_INTERVAL, /* in ms */
/* Interface Descriptor */
9, /* sizeof(usbDescrInterface): length of descriptor in bytes */
USBDESCR_INTERFACE, /* descriptor type */
1, /* index of this interface */
0, /* alternate setting for this interface */
2, /* endpoints excl 0: number of endpoint descriptors to follow */
0x0A, /* Data Interface Class Codes */
0,
0, /* Data Interface Class Protocol Codes */
0, /* string index for interface */
/* Endpoint Descriptor */
7, /* sizeof(usbDescrEndpoint) */
USBDESCR_ENDPOINT, /* descriptor type = endpoint */
0x01, /* OUT endpoint number 1 */
0x02, /* attrib: Bulk endpoint */
HW_CDC_BULK_OUT_SIZE, 0, /* maximum packet size */
0, /* in ms */
/* Endpoint Descriptor */
7, /* sizeof(usbDescrEndpoint) */
USBDESCR_ENDPOINT, /* descriptor type = endpoint */
0x81, /* IN endpoint number 1 */
0x02, /* attrib: Bulk endpoint */
HW_CDC_BULK_IN_SIZE, 0, /* maximum packet size */
0, /* in ms */
};
uchar usbFunctionDescriptor(usbRequest_t *rq)
{
if(rq->wValue.bytes[1] == USBDESCR_DEVICE){
usbMsgPtr = (uchar *)usbDescriptorDevice;
return usbDescriptorDevice[0];
}else{ /* must be config descriptor */
usbMsgPtr = (uchar *)configDescrCDC;
return sizeof(configDescrCDC);
}
}
static uchar sendEmptyFrame;
static uchar intr3Status; /* used to control interrupt endpoint transmissions */
static uchar stopbit, parity, databit;
static usbDWord_t baud;
static void resetUart(void)
{
uartInit(baud.dword, parity, stopbit, databit);
irptr = 0;
iwptr = 0;
urptr = 0;
uwptr = 0;
}
/* ------------------------------------------------------------------------- */
/* ----------------------------- USB interface ----------------------------- */
/* ------------------------------------------------------------------------- */
uchar usbFunctionSetup(uchar data[8])
{
usbRequest_t *rq = (void *)data;
if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){ /* class request type */
if( rq->bRequest==GET_LINE_CODING || rq->bRequest==SET_LINE_CODING ){
return 0xff;
/* GET_LINE_CODING -> usbFunctionRead() */
/* SET_LINE_CODING -> usbFunctionWrite() */
}
if(rq->bRequest == SET_CONTROL_LINE_STATE){
/* Report serial state (carrier detect). On several Unix platforms,
* tty devices can only be opened when carrier detect is set.
*/
if( intr3Status==0 )
intr3Status = 2;
}
/* Prepare bulk-in endpoint to respond to early termination */
if((rq->bmRequestType & USBRQ_DIR_MASK) == USBRQ_DIR_HOST_TO_DEVICE)
sendEmptyFrame = 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
/* usbFunctionRead */
/*---------------------------------------------------------------------------*/
uchar usbFunctionRead( uchar *data, uchar len )
{
data[0] = baud.bytes[0];
data[1] = baud.bytes[1];
data[2] = baud.bytes[2];
data[3] = baud.bytes[3];
data[4] = stopbit;
data[5] = parity;
data[6] = databit;
return 7;
}
/*---------------------------------------------------------------------------*/
/* usbFunctionWrite */
/*---------------------------------------------------------------------------*/
uchar usbFunctionWrite( uchar *data, uchar len )
{
/* SET_LINE_CODING */
baud.bytes[0] = data[0];
baud.bytes[1] = data[1];
baud.bytes[2] = data[2];
baud.bytes[3] = data[3];
#if USB_CFG_HAVE_USART
stopbit = data[4];
parity = data[5];
databit = data[6];
if( parity>2 )
parity = 0;
if( stopbit==1 )
stopbit = 0;
#endif
resetUart();
return 1;
}
void usbFunctionWriteOut( uchar *data, uchar len )
{
/* usb -> rs232c: transmit char */
for( ; len; len-- ) {
uartTxBufAppend(*data++);
}
/* postpone receiving next data */
if( uartTxBytesFree()<=8 )
usbDisableAllRequests();
}
static void hardwareInit(void)
{
uchar i;
/* activate pull-ups except on USB lines */
USB_CFG_IOPORT = (uchar)~((1<<USB_CFG_DMINUS_BIT)|(1<<USB_CFG_DPLUS_BIT));
/* all pins input except USB (-> USB reset) */
#ifdef USB_CFG_PULLUP_IOPORT /* use usbDeviceConnect()/usbDeviceDisconnect() if available */
USBDDR = 0; /* we do RESET by deactivating pullup */
usbDeviceDisconnect();
#else
USBDDR = (1<<USB_CFG_DMINUS_BIT)|(1<<USB_CFG_DPLUS_BIT);
#endif
for(i=0;i<20;i++){ /* 300 ms disconnect */
wdt_reset();
_delay_ms(15);
}
#ifdef USB_CFG_PULLUP_IOPORT
usbDeviceConnect();
#else
USBDDR = 0; /* remove USB reset condition */
#endif
/* USART configuration */
baud.dword = UART_DEFAULT_BPS;
stopbit = 0;
parity = 0;
databit = 8;
resetUart();
}
#if USB_CFG_HAVE_MEASURE_FRAME_LENGTH
/* ------------------------------------------------------------------------- */
/* ------------------------ Oscillator Calibration ------------------------- */
/* ------------------------------------------------------------------------- */
/* Calibrate the RC oscillator to 8.25 MHz. The core clock of 16.5 MHz is
* derived from the 66 MHz peripheral clock by dividing. Our timing reference
* is the Start Of Frame signal (a single SE0 bit) available immediately after
* a USB RESET. We first do a binary search for the OSCCAL value and then
* optimize this value with a neighboorhod search.
* This algorithm may also be used to calibrate the RC oscillator directly to
* 12 MHz (no PLL involved, can therefore be used on almost ALL AVRs), but this
* is wide outside the spec for the OSCCAL value and the required precision for
* the 12 MHz clock! Use the RC oscillator calibrated to 12 MHz for
* experimental purposes only!
*/
static void calibrateOscillator(void)
{
uchar step = 128;
uchar trialValue = 0, optimumValue;
int x, optimumDev, targetValue = (unsigned)(1499 * (double)F_CPU / 10.5e6 + 0.5);
/* do a binary search: */
do{
OSCCAL = trialValue + step;
x = usbMeasureFrameLength(); /* proportional to current real frequency */
if(x < targetValue) /* frequency still too low */
trialValue += step;
step >>= 1;
}while(step > 0);
/* We have a precision of +/- 1 for optimum OSCCAL here */
/* now do a neighborhood search for optimum value */
optimumValue = trialValue;
optimumDev = x; /* this is certainly far away from optimum */
for(OSCCAL = trialValue - 1; OSCCAL <= trialValue + 1; OSCCAL++){
x = usbMeasureFrameLength() - targetValue;
if(x < 0)
x = -x;
if(x < optimumDev){
optimumDev = x;
optimumValue = OSCCAL;
}
}
OSCCAL = optimumValue;
}
/*
Note: This calibration algorithm may try OSCCAL values of up to 192 even if
the optimum value is far below 192. It may therefore exceed the allowed clock
frequency of the CPU in low voltage designs!
You may replace this search algorithm with any other algorithm you like if
you have additional constraints such as a maximum CPU clock.
For version 5.x RC oscillators (those with a split range of 2x128 steps, e.g.
ATTiny25, ATTiny45, ATTiny85), it may be useful to search for the optimum in
both regions.
*/
void usbEventResetReady(void)
{
calibrateOscillator();
eeprom_write_byte(0, OSCCAL); /* store the calibrated value in EEPROM */
}
#endif
int main(void)
{
#if USB_CFG_HAVE_MEASURE_FRAME_LENGTH
uchar calibrationValue;
calibrationValue = eeprom_read_byte(0); /* calibration value from last time */
if(calibrationValue != 0xff){
OSCCAL = calibrationValue;
}
#endif
wdt_enable(WDTO_1S);
odDebugInit();
hardwareInit();
usbInit();
intr3Status = 0;
sendEmptyFrame = 0;
sei();
for(;;){ /* main event loop */
wdt_reset();
usbPoll();
uartPoll();
/* rs232c -> usb: transmit char */
if( usbInterruptIsReady() ) {
uchar bytesRead, *data;
bytesRead = uartRxBytesAvailable(&data);
if(bytesRead > 0 || sendEmptyFrame){
if(bytesRead >= HW_CDC_BULK_IN_SIZE) {
bytesRead = HW_CDC_BULK_IN_SIZE;
/* send an empty block after last data block to indicate transfer end */
sendEmptyFrame = 1;
}
else
sendEmptyFrame = 0;
usbSetInterrupt(data, bytesRead);
uartRxDidReadBytes(bytesRead);
}
}
/* We need to report rx and tx carrier after open attempt */
if(intr3Status != 0 && usbInterruptIsReady3()){
static uchar serialStateNotification[10] = {0xa1, 0x20, 0, 0, 0, 0, 2, 0, 3, 0};
if(intr3Status == 2){
usbSetInterrupt3(serialStateNotification, 8);
}else{
usbSetInterrupt3(serialStateNotification+8, 2);
}
intr3Status--;
}
}
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -