使用USI作为主SPI接口
代码;
lcd_drive.c
//*****************************************************************************
//
// File........: LCD_driver.c
//
// Author(s)...: ATMEL Norway
//
// Target(s)...: ATmega169
//
// Compiler....: AVR-GCC 3.3.1; avr-libc 1.0
//
// Description.: Functions used to control the AVR Butterfly LCD
//
// Revisions...: 1.0
//
// YYYYMMDD - VER. - COMMENT - SIGN.
//
// 20021015 - 1.0 - Written for STK502 - JLL
// 20030116 - 2.0 - Code adapted to AVR Butterfly - KS
// 20031009 port to avr-gcc/avr-libc - M.Thomas
//
//*****************************************************************************#define REDUCED// Include files.
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <avr/interrupt.h>// mt - for gButtonTimeout
// #include "button.h"#include "LCD_driver.h"#ifndef BOOL
#define BOOL char
#define FALSE 0
#define TRUE (!FALSE)
#endif// Variable from "button.c" to prevent button-bouncing
extern unsigned char gButtonTimeout; volatile char gAutoPressJoystick = FALSE;// Used to indicate when the LCD interrupt handler should update the LCD
// mt jw char gLCD_Update_Required = FALSE;
volatile char gLCD_Update_Required = FALSE;// LCD display buffer (for double buffering).
volatile char LCD_Data[LCD_REGISTER_COUNT];// Buffer that contains the text to be displayed
// Note: Bit 7 indicates that this character is flashing
volatile char gTextBuffer[TEXTBUFFER_SIZE];// Only six letters can be shown on the LCD.
// With the gScroll and gScrollMode variables,
// one can select which part of the buffer to show
volatile signed char gScroll;
volatile char gScrollMode;Start-up delay before scrolling a string over the LCD
char gLCD_Start_Scroll_Timer = 0;// The gFlashTimer is used to determine the on/off
// timing of flashing characters
volatile char gFlashTimer = 0;// Turns on/off the colons on the LCD
char gColon = 0;// Look-up table used when converting ASCII to
// LCD display data (segment control)
// mt __flash unsigned int LCD_character_table[] =
unsigned int LCD_character_table[] PROGMEM =
{0x0A51, // '*' (?)0x2A80, // '+'0x0000, // ',' (Not defined)0x0A00, // '-'0x0A51, // '.' Degree sign0x0000, // '/' (Not defined)0x5559, // '0'0x0118, // '1'0x1e11, // '20x1b11, // '30x0b50, // '40x1b41, // '50x1f41, // '60x0111, // '70x1f51, // '80x1b51, // '9'0x0000, // ':' (Not defined)0x0000, // ';' (Not defined)0x0000, // '<' (Not defined)0x0000, // '=' (Not defined)0x0000, // '>' (Not defined)0x0000, // '?' (Not defined)0x0000, // '@' (Not defined)0x0f51, // 'A' (+ 'a')0x3991, // 'B' (+ 'b')0x1441, // 'C' (+ 'c')0x3191, // 'D' (+ 'd')0x1e41, // 'E' (+ 'e')0x0e41, // 'F' (+ 'f')0x1d41, // 'G' (+ 'g')0x0f50, // 'H' (+ 'h')0x2080, // 'I' (+ 'i')0x1510, // 'J' (+ 'j')0x8648, // 'K' (+ 'k')0x1440, // 'L' (+ 'l')0x0578, // 'M' (+ 'm')0x8570, // 'N' (+ 'n')0x1551, // 'O' (+ 'o')0x0e51, // 'P' (+ 'p')0x9551, // 'Q' (+ 'q')0x8e51, // 'R' (+ 'r')0x9021, // 'S' (+ 's')0x2081, // 'T' (+ 't')0x1550, // 'U' (+ 'u')0x4448, // 'V' (+ 'v')0xc550, // 'W' (+ 'w')0xc028, // 'X' (+ 'x')0x2028, // 'Y' (+ 'y')0x5009, // 'Z' (+ 'z')0x0000, // '[' (Not defined)0x0000, // '\' (Not defined)0x0000, // ']' (Not defined)0x0000, // '^' (Not defined)0x0000 // '_'
};/*****************************************************************************
*
* Function name : LCD_Init
*
* Returns : None
*
* Parameters : None
*
* Purpose : Initialize LCD_displayData buffer.
* Set up the LCD (timing, contrast, etc.)
*
*****************************************************************************/
void LCD_Init (void)
{LCD_AllSegments(FALSE); // Clear segment buffer.LCD_CONTRAST_LEVEL(LCD_INITIAL_CONTRAST); //Set the LCD contrast level// Select asynchronous clock source, enable all COM pins and enable all// segment pins.LCDCRB = (1<<LCDCS) | (3<<LCDMUX0) | (7<<LCDPM0);// Set LCD prescaler to give a framerate of 32,0 HzLCDFRR = (0<<LCDPS0) | (7<<LCDCD0); LCDCRA = (1<<LCDEN) | (1<<LCDAB); // Enable LCD and set low power waveform//Enable LCD start of frame interruptLCDCRA |= (1<<LCDIE);gLCD_Update_Required = FALSE;}/*****************************************************************************
*
* Function name : LCD_WriteDigit(char c, char digit)
*
* Returns : None
*
* Parameters : Inputs
* c: The symbol to be displayed in a LCD digit
* digit: In which digit (0-5) the symbol should be displayed
* Note: Digit 0 is the first used digit on the LCD,
* i.e LCD digit 2
*
* Purpose : Stores LCD control data in the LCD_displayData buffer.
* (The LCD_displayData is latched in the LCD_SOF interrupt.)
*
*****************************************************************************/
void LCD_WriteDigit(char c, char digit)
{unsigned int seg = 0x0000; // Holds the segment patternchar mask, nibble;volatile char *ptr;char i;if (digit > 5) // Skip if digit is illegalreturn;//Lookup character table for segmet dataif ((c >= '*') && (c <= 'z')){// c is a letterif (c >= 'a') // Convert to upper casec &= ~0x20; // if necessarryc -= '*';//mt seg = LCD_character_table[c];seg = (unsigned int) pgm_read_word(&LCD_character_table[(uint8_t)c]); }// Adjust mask according to LCD segment mappingif (digit & 0x01)mask = 0x0F; // Digit 1, 3, 5elsemask = 0xF0; // Digit 0, 2, 4ptr = LCD_Data + (digit >> 1); // digit = {0,0,1,1,2,2}for (i = 0; i < 4; i++){nibble = seg & 0x000F;seg >>= 4;if (digit & 0x01)nibble <<= 4;*ptr = (*ptr & mask) | nibble;ptr += 5;}
}/*****************************************************************************
*
* Function name : LCD_AllSegments(unsigned char input)
*
* Returns : None
*
* Parameters : show - [TRUE;FALSE]
*
* Purpose : shows or hide all all LCD segments on the LCD
*
*****************************************************************************/
void LCD_AllSegments(char show)
{unsigned char i;if (show)show = 0xFF;// Set/clear all bits in all LCD registersfor (i=0; i < LCD_REGISTER_COUNT; i++)*(LCD_Data + i) = show;
}/*****************************************************************************
*
* LCD Interrupt Routine
*
* Returns : None
*
* Parameters : None
*
* Purpose: Latch the LCD_displayData and Set LCD_status.updateComplete
*
*****************************************************************************/SIGNAL(SIG_LCD)
{static char LCD_timer = LCD_TIMER_SEED;char c;char c_flash;char flash;char EOL;unsigned char i;#ifndef REDUCEDstatic char timeout_count;static char auto_joystick_count;
#endifc_flash=0; // mt#ifndef REDUCED
/**************** Button timeout for the button.c, START ****************/if(!gButtonTimeout){timeout_count++;if(timeout_count > 3){gButtonTimeout = TRUE;timeout_count = 0;}}/**************** Button timeout for the button.c, END ******************//**************** Auto press joystick for the main.c, START *************/if(gAutoPressJoystick == AUTO){auto_joystick_count++;if(auto_joystick_count > 16){gAutoPressJoystick = TRUE;auto_joystick_count = 15;}}elseauto_joystick_count = 0;/**************** Auto press joystick for the main.c, END ***************/
#endifLCD_timer--; // Decreased every LCD frameif (gScrollMode){// If we are in scroll mode, and the timer has expired,// we will update the LCDif (LCD_timer == 0){if (gLCD_Start_Scroll_Timer == 0){gLCD_Update_Required = TRUE;}elsegLCD_Start_Scroll_Timer--;}}else { // if not scrolling,// disble LCD start of frame interrupt
// cbi(LCDCRA, LCDIE); //DEBUGgScroll = 0;}EOL = FALSE;if (gLCD_Update_Required == TRUE){// Duty cycle of flashing charactersif (gFlashTimer < (LCD_FLASH_SEED >> 1))flash = 0;elseflash = 1;// Repeat for the six LCD charactersfor (i = 0; i < 6; i++){if ((gScroll+i) >= 0 && (!EOL)){// We have some visible charactersc = gTextBuffer[i + gScroll];c_flash = c & 0x80 ? 1 : 0;c = c & 0x7F;if (c == '\0')EOL = i+1; // End of character data}elsec = ' ';// Check if this character is flashingif (c_flash && flash)LCD_WriteDigit(' ', i);elseLCD_WriteDigit(c, i);}// Copy the segment buffer to the real segmentsfor (i = 0; i < LCD_REGISTER_COUNT; i++)*(pLCDREG + i) = *(LCD_Data+i);// Handle colonif (gColon)*(pLCDREG + 8) = 0x01;else*(pLCDREG + 8) = 0x00;// If the text scrolled off the display,// we have to start over again.if (EOL == 1)gScroll = -6;elsegScroll++;// No need to update anymoregLCD_Update_Required = FALSE;}// LCD_timer is used when scrolling textif (LCD_timer == 0){
/* if ((gScroll <= 0) || EOL)LCD_timer = LCD_TIMER_SEED/2;else*/LCD_timer = LCD_TIMER_SEED;}// gFlashTimer is used when flashing charactersif (gFlashTimer == LCD_FLASH_SEED)gFlashTimer= 0;elsegFlashTimer++;}
lcd_functions.c
//*****************************************************************************
//
// File........: LCD_functions.c
//
// Author(s)...: ATMEL Norway
//
// Target(s)...: ATmega169
//
// Compiler....: AVR-GCC 3.3.1; avr-libc 1.0
//
// Description.: Additional LCD functions, scrolling text and write data
//
// Revisions...: 1.0
//
// YYYYMMDD - VER. - COMMENT - SIGN.
//
// 20021015 - 1.0 - Created - LHM
// 20030116 - 2.0 - Code adapted to AVR Butterflyup - KS
// 20031009 port to avr-gcc/avr-libc - M.Thomas
//
//*****************************************************************************#define REDUCED// Include files
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include "LCD_driver.h"
#include "LCD_functions.h"#ifndef REDUCED
#include "BCD.h"
// mt only for KEY_* and ST_OPTIONS_DISPLAY* definitions:
#include "main.h"
#endif#define FALSE 0
#define TRUE (!FALSE)// mt char CONTRAST = LCD_INITIAL_CONTRAST;
uint8_t CONTRAST = LCD_INITIAL_CONTRAST;// Start-up delay before scrolling a string over the LCD. "LCD_driver.c"
extern char gLCD_Start_Scroll_Timer;/****************************************************************************
*
* Function name : LCD_puts_f
*
* Returns : None
*
* Parameters : pFlashStr: Pointer to the string in flash
* scrollmode: Not in use
*
* Purpose : Writes a string stored in flash to the LCD
*
*****************************************************************************/// mt void LCD_puts_f(char __flash *pFlashStr, char scrollmode)
void LCD_puts_f(const char *pFlashStr, char scrollmode)
{// char i;uint8_t i;while (gLCD_Update_Required); // Wait for access to buffer// mt: for (i = 0; pFlashStr[i] && i < TEXTBUFFER_SIZE; i++)for (i = 0; (const char)(pgm_read_byte(&pFlashStr[i])) && i < TEXTBUFFER_SIZE; i++){// mt: gTextBuffer[i] = pFlashStr[i];gTextBuffer[i] = pgm_read_byte(&pFlashStr[i]);}gTextBuffer[i] = '\0';if (i > 6){gScrollMode = 1; // Scroll if text is longer than display sizegScroll = 0;gLCD_Start_Scroll_Timer = 3; //Start-up delay before scrolling the text}else{gScrollMode = 0; gScroll = 0;}gLCD_Update_Required = 1;
}/****************************************************************************
*
* Function name : LCD_puts
*
* Returns : None
*
* Parameters : pStr: Pointer to the string
* scrollmode: Not in use
*
* Purpose : Writes a string to the LCD
*
*****************************************************************************/
void LCD_puts(char *pStr, char scrollmode)
{uint8_t i; // char i;while (gLCD_Update_Required); // Wait for access to bufferfor (i = 0; pStr[i] && i < TEXTBUFFER_SIZE; i++){gTextBuffer[i] = pStr[i];}gTextBuffer[i] = '\0';if (i > 6){gScrollMode = 1; // Scroll if text is longer than display sizegScroll = 0;gLCD_Start_Scroll_Timer = 3; //Start-up delay before scrolling the text}else{gScrollMode = 0; gScroll = 0;}gLCD_Update_Required = 1;
}/****************************************************************************
*
* Function name : LCD_putc
*
* Returns : None
*
* Parameters : digit: Which digit to write on the LCD
* character: Character to write
*
* Purpose : Writes a character to the LCD
*
*****************************************************************************/
// mt void LCD_putc(char digit, char character)
void LCD_putc(uint8_t digit, char character)
{if (digit < TEXTBUFFER_SIZE)gTextBuffer[digit] = character;
}/****************************************************************************
*
* Function name : LCD_Clear
*
* Returns : None
*
* Parameters : None
*
* Purpose : Clear the LCD
*
*****************************************************************************/
void LCD_Clear(void)
{uint8_t i; // char i;for (i=0; i<TEXTBUFFER_SIZE; i++)gTextBuffer[i] = ' ';
}/****************************************************************************
*
* Function name : LCD_Colon
*
* Returns : None
*
* Parameters : show: Enables the colon if TRUE, disable if FALSE
*
* Purpose : Enable/disable colons on the LCD
*
*****************************************************************************/
void LCD_Colon(char show)
{gColon = show;
}/****************************************************************************
*
* Function name : LCD_UpdateRequired
*
* Returns : None
*
* Parameters : update: TRUE/FALSE
* scrollmode: not in use
*
* Purpose : Tells the LCD that there is new data to be presented
*
*****************************************************************************/
void LCD_UpdateRequired(char update, char scrollmode)
{while (gLCD_Update_Required);gScrollMode = scrollmode;gScroll = 0;gLCD_Update_Required = update;
}/****************************************************************************
*
* Function name : LCD_FlashReset
*
* Returns : None
*
* Parameters : None
*
* Purpose : This function resets the blinking cycle of a flashing digit
*
*****************************************************************************/
void LCD_FlashReset(void)
{gFlashTimer = 0;
}#ifndef REDUCED/****************************************************************************
*
* Function name : SetContrast
*
* Returns : char ST_state (to the state-machine)
*
* Parameters : char input (from joystick)
*
* Purpose : Adjust the LCD contrast
*
*****************************************************************************/
char SetContrast(char input)
{static char enter = 1;char CH, CL;if (enter){LCD_Clear();enter = 0;}CH = CHAR2BCD2(CONTRAST);CL = (CH & 0x0F) + '0';CH = (CH >> 4) + '0';LCD_putc(0, 'C');LCD_putc(1, 'T');LCD_putc(2, 'R');LCD_putc(3, ' ');LCD_putc(4, CH);LCD_putc(5, CL);LCD_UpdateRequired(TRUE, 0);if (input == KEY_PLUS)CONTRAST++;else if (input == KEY_MINUS)CONTRAST--;if (CONTRAST == 255)CONTRAST = 0;if (CONTRAST > 15)CONTRAST = 15;LCD_CONTRAST_LEVEL(CONTRAST);if (input == KEY_ENTER){enter = 1;return ST_OPTIONS_DISPLAY_CONTRAST;}return ST_OPTIONS_DISPLAY_CONTRAST_FUNC;
}#endif
// REDUCED
mian.c
/*《AVR专题精选》随书例程3.通信接口使用技巧项目:使用USI作为主SPI接口文件:main.c说明:主程序文件. 在AVR Bufferfly上演示USI做主SPI接口用法控制EEPROM 25AA010按键: 上 数据加下 数据减左 读取数据右 写数据中 随机数据作者:邵子扬时间:2012年12月15日*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include <stdio.h>// 系统时钟频率
#define F_CPU 1000000UL#include <util/delay.h>#include "LCD_functions.h"
#include "LCD_driver.h"#include "macromcu.h"// 定义端口
#define SS B, 0
#define SCK E, 4
#define MOSI E, 6
#define MISO E, 5// SPI初始化
void SPI_init()
{PINSET(SS);USIDR = 0;USISR = (1<<USIOIF);USICR = (1<<USIWM0)|(1<<USICLK)|(1<<USICLK);
}// SPI读写函数
unsigned char SPI_WR(unsigned char dat)
{unsigned char i;USIDR = dat; // 数据放入USI数据寄存器for(i = 0; i < 8; i++) // 软件方式驱动时钟信号{USICR = (1<<USIWM0)|(1<<USITC);USICR = (1<<USIWM0)|(1<<USITC)|(1<<USICLK);}return USIDR; // 返回数据
}// 按键掩码
#define PINB_MASK ((1<<PINB4)|(1<<PINB6)|(1<<PINB7))
#define PINE_MASK ((1<<PINE2)|(1<<PINE3))// 读取按键状态
unsigned char getkey()
{unsigned char t;t = (~PINB) & PINB_MASK;t |= (~PINE) & PINE_MASK;return t;
}unsigned char dat = 0, cnt;
char s[] = " 00";// HEX数据转换为字符
unsigned char hexchr(unsigned char dat)
{if(dat > 15)return '0';if(dat < 10)return dat + '0';elsereturn dat - 10 + 'A';
}// HEX数据转换字符串
void hexbuf(unsigned char dat, char *buf)
{buf[0] = hexchr(dat/16);buf[1] = hexchr(dat%16);
}// 从EEPROM地址0读取一个字节
unsigned char ReadByte()
{unsigned char t;PINCLR(SS); // 片选信号SPI_WR(0x03); // 发读命令SPI_WR(0x00); // 地址高位SPI_WR(0x00); // 地址低位t = SPI_WR(0); // 数据PINSET(SS); // 禁止片选return t;
}// 写入一个字节到EEPROM地址0
void WriteByte(unsigned char dat)
{PINCLR(SS); // 使能片选SPI_WR(0x02); // 发送写命令SPI_WR(0x00); // 地址高位SPI_WR(0x00); // 地址低位SPI_WR(dat); // 数据PINSET(SS); // 禁止片选
}// 允许写入EEPROM
void WREN(void)
{PINCLR(SS); // 片选SPI_WR(0x06); // 发送wren命令PINSET(SS);
}// 主程序
int main()
{// IO 初始化PINDIR(SS, PIN_OUTPUT);PINDIR(SCK, PIN_OUTPUT);PINDIR(MOSI, PIN_OUTPUT);PINDIR(MISO, PIN_OUTPUT);PORTB |= PINB_MASK;PORTE |= PINE_MASK;// SPI初始化SPI_init(0, 0);// AVR Butterfly LCD初始化LCD_Init();// 开中断sei();// 显示地址和数据hexbuf(dat, s+4);LCD_puts(s, 0);while(1){_delay_ms(200); // 延时cnt++;if(getkey()) // 读取按键{switch(getkey()){case 0x40://updat++; // 数据递增s[0] = ' ';break;case 0x80://dndat--; // 数据递减s[0] = ' ';break;case 0x04://lefts[0] = 'R'; // 左键代表读取数据dat = ReadByte();break;case 0x08://rights[0] = 'W'; // 右键写入数据WREN();WriteByte(dat);break;case 0x10://enter//dat = ReadStatus();//hexbuf(dat, s+2);dat = cnt; // 回车键随机设置数据break;}hexbuf(dat, s+4);LCD_puts(s, 0);}}return 0;
}
仿真效果图: