⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 main.c

📁 This is a software implementation of the USB low-speed protocol for the Atmel ATtiny microcontrolle
💻 C
字号:
// ======================================================================// IR receiver and LCD controller with USB interface.//// Read IR mark/space patterns from a TSOP1738 on the ICP input pin.// Mark and space periods are measured in units of 12Mhz/1024, which is// 85.33 us. Up to 35 periods are stored in a buffer, where they can// be read via a USB request. The buffer layout and USB requests are// compatible with the "IgorPlug-USB" device, which is supported by LIRC// through the "igorplugusb" driver. This implementation should also// work with that driver. If IgorPlug-USB compatibility is not required,// the resolution could be improved by reducing SCALING to 9, and you// could also get rid of the additional endpoint (USBTINY_ENDPOINT).//// There are some IR protocols with very long packets length, such as// the "NEC" protocol (http://www.sbprojects.com/knowledge/ir/nec.htm).// This protocol uses 67 mark/space periods, which is too much for the// limited amount of RAM in the ATtiny2313. Simply truncating the// packet to the amount of available buffer space is not an option,// because the significant bits are at the end of the packet. Instead,// a buffer of 35 bytes (IR_MAX) is used, of which the last 16 bytes// (IR_WRAP) are used as a circular buffer. Because both the address// (16 periods) and the command code (16 periods) are sent twice (normal// and inverted), the wrapping will result in a buffer with both the// device address and the inverted command code. Other IR protocols are// typically shorter than 35 periods.//// 16-bit Timer1 is initialized in normal mode with a clock of 12MHz/8.// Both the positive and negative edges of the IR input signal on the// ICP input pin trigger an input capture interrupt, because the edge// selection is toggled on every interrupt. Output compare register A// is used for a timeout to detect the end of an IR transmission.// The timeout value is updated on every edge. When at least 4 edges// were detected when a timeout occurs, the packet count ir.count is// incremented and the ir.length byte is set to the packed length. This// byte is checked when a read request is received via USB to see if a// packet is available//// The two interrupt handlers in this file run for more than 28 cycles,// so they need to reenable interrupts to allow USB interrupts to be// handled in time. To avoid nested IR interrupts, the input capture and// output compare interrupts are disabled before the global interrupt// flag is set. In the input capture interrupt handler, the interrupts// are disabled for a maximum of 26, 27 and 21 cycles, and in the output// compare interrupt handler for 21, 23 and 21 cycles. Unfortunately,// it was not possible to meet the 28 cycle maximum for the input// capture interrupt handler without replacing the compiler generated// prolog and epilog code by inline assembly, which makes the code a// little fragile.//// The IR receiver is disabled during an IR buffer read, so that a// packet that is being tranmitted to the host, is not overwritten by// the next packet. When a transfer is in progress, 'inpos' has a value// other than 0xff. When the buffer is read completely, the IR receiver// is reenabled again, but when the next IR packet has already started,// this packed is discarded by setting 'edges' to 0xff.//// There are two USB request codes to control the LCD, one to write// instructions, and one to write data to the display. OUT transfers// are used to transfer up to 255 bytes for a single request.//// Stack space calculations for gcc-3.4.3 and gcc-4.1.0://// 3.4.3   4.1.0// -----   -----//   0       0       main//   0       0           lcd_init (inlined)//   2       2           usb_init//   0       0           ir_init (inlined)//   6       5           usb_poll//   0       0               usb_receive (inlined)//   2       2                   usb_setup//   5       5                   usb_out//   4       4                       lcd_write//   2       2                           lcd_read4//   2       2                           lcd_write4//   0       0               usb_transmit (inlined)//   2       4                   usb_int//   2       2                   crc//// Maximum stack space is 6+5+4+2=17 for gcc-3.4.3 and 5+5+4+2=16 for// gcc-4.1.0. Total stack usage:// - USB interrupt:			11 bytes// - IR input capture interrupt:	11 bytes// - IR output compare interrupt: 	 7 bytes// - main():				17 bytes// The two IR interrupt handler cannot be active simultaneously, so the// maximum stack space is 17+11+11=39 bytes.//// NOTE: The above cycle and stack size figures were obtained by//       inspecting the code generated by gcc-3.4.3 and gcc-4.1.0.//       Other compiler versions may generate different code, leading//       to different figures. Likewise, the replacement prolog and//       epilog code may have to be adapted when a different compiler//       version allocates different registers.//// Copyright 2006-2008 Dick Streefland//// This is free software, licensed under the terms of the GNU General// Public License as published by the Free Software Foundation.// ======================================================================#include <avr/io.h>#include <avr/interrupt.h>#include "usb.h"// ----------------------------------------------------------------------// IR receiver definitions// ----------------------------------------------------------------------#define	TIMEOUT		10500	// IR transmission timeout in us#define	IR_MAX		35	// maximum number of IR data bytes#define	IR_WRAP		16	// wrap area at end (for 67 byte NEC protocol)#define	SCALING		10	// 9: 42.67 us period, 10: 85.33 us period#define	LED		5	// PORTD bit number for LED#define	PULLUP		3	// PORTD bit number for D- pullupenum{	// Generic requests	USBTINY_ECHO,		// echo test	// IgorPlug-USB requests	IGORPLUG_CLEAR,		// clear IR data	IGORPLUG_READ,		// read IR data (wValue: offset)	// LCD requests	LCD_INSTR = 20,		// write instructions to LCD (via OUT)	LCD_DATA,		// write data to LCD (via OUT)};static	byte_t	inpos = 0xff;	// read position for usb_in(), or 0xffstatic	byte_t	edges;		// incremented for each edgestatic	struct			// IgorPlug-USB compatible data layout{	byte_t	length;		// length of data[]	byte_t	count;		// incremented for each IR packet	byte_t	offset;		// not used	byte_t	data[IR_MAX];	// mark/space periods}		ir;// ----------------------------------------------------------------------// LCD definitions// ----------------------------------------------------------------------#define	LCD_PRESENT	1	// set to 0 to remove LCD support#define	DDR		DDRB#define	PORT		PORTB#define	PIN		PINB#define	D4		0#define	RS		4#define	RW		5#define	E		6#define	MASK_D4		(0xf << D4)#define	MASK_RS		(1 << RS)#define	MASK_RW		(1 << RW)#define	MASK_E		(1 << E)#ifndef	ICIE1#define	ICIE1		TICIE1#endif// ----------------------------------------------------------------------// Handler for timer1 input capture interrupt: edge on IR input// ----------------------------------------------------------------------__attribute__((signal,naked))			// interrupts are DISABLEDextern	void	SIG_INPUT_CAPTURE1 ( void ){	static	uint_t	prev;	uint_t		stamp;	byte_t		delta;	byte_t		e;	asm volatile(		"push	r23\n"		"in	r23,__SREG__\n"		"push	r18\n"		"push	r19\n"		"push	r24\n"		"push	r25\n"	:: );	stamp = ICR1;				// get time stamp	TIMSK = 0;				// disable both IR interrupts	TCCR1B ^= _BV(ICES1);			// toggle edge detector	sei();					// allow USB interrupt	PORTD |= _BV(LED);			// switch LED on	asm volatile(		"push	r20\n"		"push	r21\n"		"push	r30\n"		"push	r31\n"	:: );	delta = (stamp - prev + (1 << (SCALING-3-1))) >> (SCALING-3);	asm volatile("" : : "r"(delta) );	// calculate delta (gcc-4.1.0)	prev  = stamp;	OCR1A = stamp + 12L * TIMEOUT / 8;	// update timeout value	cli();					// enter critical region	e = edges;	if	( e != 0xff )			// packet should not be ignored?	{		if	( e > IR_MAX )		// packet too long for buffer?		{			e -= IR_WRAP;		// wrap, don't truncate		}		if	( inpos == 0xff )	// update ir only when USB idle		{			ir.length = 0;		// discard previous packet			if	( e > 0 )			{				ir.data[e - 1] = delta;			}		}		edges = e + 1;	}	sei();					// allow USB interrupt	asm volatile("nop");			// reduce latency to 1 cycle	asm volatile(		"pop	r31\n"		"pop	r30\n"		"pop	r21\n"		"pop	r20\n"	:: );	cli();	TIMSK = _BV(OCIE1A) | _BV(ICIE1);	// reenable IR interrupts	asm volatile(		"pop	r25\n"		"pop	r24\n"		"pop	r19\n"		"pop	r18\n"		"out	__SREG__,r23\n"		"pop	r23\n"		"reti\n"	:: );}// ----------------------------------------------------------------------// Handler for timer1 output compare A interrupt: IR transmission timeout// ----------------------------------------------------------------------__attribute__((signal))				// interrupts are DISABLEDextern	void	SIG_OUTPUT_COMPARE1A ( void ){	TIMSK = 0;				// disable both IR interrupts	sei();					// allow USB interrupt	PORTD &= ~_BV(LED);			// switch LED off	cli();					// enter critical region	if	(  edges >= 4			// at least two pulses (NEC)		&& edges != 0xff		// packet should not be ignored		&& inpos == 0xff		// update ir only when USB idle		)	{		ir.count++;		ir.length = edges - 1;		// new packet is complete	}	edges = 0;	sei();					// allow USB interrupt	TCCR1B &= ~_BV(ICES1);			// reset to negative edge	cli();	TIMSK = _BV(OCIE1A) | _BV(ICIE1);	// reenable IR interrupts}// ----------------------------------------------------------------------// Initialize the IR receiver.// ----------------------------------------------------------------------static	void	ir_init ( void ){	DDRD |= _BV(LED);	TCCR1B = _BV(ICNC1)	// noise canceler, trigger on negative edge	       | _BV(CS11);	// clock source clk/8	TIMSK  = _BV(OCIE1A)	// output compare 1A match interrupt enable	       | _BV(ICIE1);	// input capture 1 interrupt enable}#if	LCD_PRESENT// ----------------------------------------------------------------------// Delay <count> times 100 microseconds.// ----------------------------------------------------------------------static	void	lcd_delay100u ( byte_t count ){	asm volatile(		"0:	ldi	r25, 240\n"		"1:	rjmp	2f\n"		"2:	dec	r25\n"		"	brne	1b\n"	// 240 * 5 cycles = 100us		"	dec	r24\n"		"	brne	0b\n"	);}// ----------------------------------------------------------------------// Write 4 bits to the LCD.// ----------------------------------------------------------------------static	void	lcd_write4 ( byte_t data ){	PORT = (PORT & ~ MASK_D4) | (data << D4);	PORT |= MASK_E;			// E high period: > 230ns	DDR  = MASK_RS | MASK_RW | MASK_E | MASK_D4; // Dx setup time: > 80ns	PORT &= ~ MASK_E;		// hold time: > 10ns	DDR  = MASK_RS | MASK_RW | MASK_E;}// ----------------------------------------------------------------------// Read 4 bits from the LCD.// ----------------------------------------------------------------------static	byte_t	lcd_read4 ( void ){	byte_t	save;	byte_t	data;	save = PORT;	PORT = MASK_RW;			// address setup time: > 40ns	PORT |= MASK_E;			// E high period: > 230ns	asm volatile("rjmp 0f\n0:");	data = (PIN & MASK_D4) >> D4;	// data access time: > 160ns	PORT = save;	return data;}// ----------------------------------------------------------------------// Write a byte to the LCD.// ----------------------------------------------------------------------static	void	lcd_write ( byte_t b ){	byte_t	h;	do	{		h = lcd_read4();		(void) lcd_read4();	} while	( h & 0x08 );	lcd_write4( b >> 4 );	lcd_write4( b & 15 );}// ----------------------------------------------------------------------// Initialize the LCD.// ----------------------------------------------------------------------static	void	lcd_init ( void ){	DDR = MASK_RS | MASK_RW | MASK_E;	PORT = 0;	//lcd_delay100u( 150 );	-- skipped because of RESET delay	lcd_write4( 3 );	lcd_delay100u( 41 );	lcd_write4( 3 );	lcd_delay100u( 1 );	lcd_write( 0x32 );	// switch to 4-bit mode	lcd_write( 0x28 );	// function set: 4-bit mode, two lines	lcd_write( 0x01 );	// clear display	lcd_write( 0x06 );	// entry mode: increment, no shift	lcd_write( 0x0c );	// display control: on, no cursor}#endif	/* LCD_PRESENT */// ----------------------------------------------------------------------// Handle a non-standard SETUP packet.// ----------------------------------------------------------------------extern	byte_t	usb_setup ( byte_t data[8] ){	byte_t	req;	byte_t	r;	r = 0;	// Generic requests	req = data[1];	if	( req == USBTINY_ECHO )	{		r = 8;	}	// IgorPlug-USB requests	if	( req == IGORPLUG_CLEAR )	{		ir.length = 0;		inpos = 0xff;	}	if	( req == IGORPLUG_READ )	{		cli();		if	( ir.length > 0 )		{			inpos = data[4];			r = 0xff;	// call usb_in() to get the data		}		else		{			data[0] = 0;			r = 1;		}		sei();	}	// LCD requests	if	( req == LCD_INSTR )	{		PORT &= ~ MASK_RS;	// data will be received by usb_out()	}	if	( req == LCD_DATA )	{		PORT |= MASK_RS;	// data will be received by usb_out()	}	return r;}// ----------------------------------------------------------------------// Handle an IN packet.// ----------------------------------------------------------------------extern	byte_t	usb_in ( byte_t* data, byte_t len ){	byte_t	n;	byte_t	max;	max = ir.length + 3;	n = 0;	while	( n < len )	{		if	( inpos >= max )	// end of packet?		{			cli();			if	( edges )	// next packet already started?			{				edges = 0xff;	// ignore remainder of packet			}			inpos = 0xff;		// reenable receiver			sei();			break;		}		data[n++] = (& ir.length)[inpos++];	}	return n;}// ----------------------------------------------------------------------// Handle an OUT packet.// ----------------------------------------------------------------------extern	void	usb_out ( byte_t* data, byte_t len ){#if	LCD_PRESENT	while	( len )	{		lcd_write( *data++ );		len--;	}#endif	/* LCD_PRESENT */}// ----------------------------------------------------------------------// Main// ----------------------------------------------------------------------__attribute__((naked))		// suppress redundant SP initializationextern	int	main ( void ){	PORTD |= _BV(PULLUP);	DDRD  |= _BV(PULLUP);	// enable pullup on D-#if	LCD_PRESENT	lcd_init();#endif	/* LCD_PRESENT */	usb_init();	ir_init();	for	( ;; )	{		usb_poll();	}	return 0;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -