运行结果不能显示预期的汉字
//-----------------------------------------------------------------
// 名称:用SPI接口读写AT25F4096子程序
//-----------------------------------------------------------------
#define F_CPU 4000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/sfr_defs.h>
#include <avr/interrupt.h>
#include <string.h>
#include <stdio.h>
//#define INT8 signed char
#define INT8U unsigned char
#define INT16U unsigned int
#define INT32U unsigned long
//AT25F4096指令集
#define WREN 0x06 //使能写
#define WRDI 0x04 //禁止写
#define RDSR 0x05 //读状态
#define WRSR 0x01 //写状态
#define READ 0x03 //读字节
#define PROGRAM 0x02 //写字节
#define SECTOR_ERASE 0x52 //删除区域数据
#define CHIP_ERASE 0x62 //删除芯片数据
#define RDID 0x15 //读厂商与产品ID
//SPI使能与禁用
#define SPI_EN() (PORTB &= 0xEF)
#define SPI_DI() (PORTB |= 0x10)
//-----------------------------------------------------------------
// SPI主机初始化
//-----------------------------------------------------------------
void SPI_MasterInit()
{
//SPI接口配置
DDRB=0b10110000;PORTB=0xFF;
//SPI使能,主机模式,16分频
SPCR|=_BV(SPE)|_BV(MSTR)|_BV(SPR0);
}
//-----------------------------------------------------------------
// SPI数据传输
//-----------------------------------------------------------------
INT8U SPI_Transmit(INT8U dat)
{
SPDR=dat; //启动数据传输
while(!(SPSR &_BV(SPIF))); //等待结束
SPSR |=_BV(SPIF); //清中断标志
return SPDR;
}
//-----------------------------------------------------------------
// 读AT25F4096芯片状态
//-----------------------------------------------------------------
INT8U Read_SPI_Status()
{
INT8U status;
SPI_EN();
SPI_Transmit(RDSR); //发送读状态指令
status=SPI_Transmit(0xFF);
SPI_DI();
return status;
}
//-----------------------------------------------------------------
// AT25F4096忙等待
//-----------------------------------------------------------------
void Busy_Wait()
{
while(Read_SPI_Status() & 0x01); //忙等待
}
//-----------------------------------------------------------------
// 向AT25F4096写入三个字节的地址0x000000-0x07FFFF (有效位为19位)
//-----------------------------------------------------------------
void Write_3_Bytes_AT25F4096_Address(INT32U addr)
{
SPI_Transmit((INT8U)(addr>>16));
SPI_Transmit((INT8U)(addr>>8));
SPI_Transmit((INT8U)(addr));
}
//-----------------------------------------------------------------
// 从指定地址读单字节
//-----------------------------------------------------------------
INT8U Read_Byte_FROM_AT25F4096(INT32U addr)
{
INT8U dat;
SPI_EN();
SPI_Transmit(READ); //发送读指令
Write_3_Bytes_AT25F4096_Address(addr); //发送3字节地址
dat=SPI_Transmit(0xFF); //读取字节数据
SPI_DI();
return dat;
}
//-----------------------------------------------------------------
// 从指定地址读多字节到缓冲
//-----------------------------------------------------------------
void Read_Some_Bytes_FROM_AT25F4096(INT32U addr, INT8U *p, INT16U len)
{
INT16U i;
SPI_EN();
SPI_Transmit(READ); //发送读指令
Write_3_Bytes_AT25F4096_Address(addr); //发送3字节地址
for(i=0;i<len;i++) //读数据序列
p[i]=SPI_Transmit(0xFF);
SPI_DI();
}
//-----------------------------------------------------------------
// 向AT25F4096指定地址写入单字节数据
//-----------------------------------------------------------------
void Write_Byte_TO_AT25F4096(INT32U addr,INT8U dat)
{
SPI_EN();
SPI_Transmit(WREN); //使能写
SPI_DI();
Busy_Wait();
SPI_EN();
SPI_Transmit(PROGRAM); //写指令
Write_3_Bytes_AT25F4096_Address(addr); //发送3字节地址
SPI_Transmit(dat); //写字节数据
SPI_DI();
Busy_Wait();
}
//-----------------------------------------------------------------
// 名称: 能接收串口信息的带中英文硬字库的80*16点阵显示屏
//-----------------------------------------------------------------
// 说明: 本例运行时,点阵屏将滚动显示一组固定信息
// 当接收到串口发送来的中英文/全角/半角字符时,点屏将开始
// 滚动显示所接收到的信息.
//
//-----------------------------------------------------------------
//74595及74154相关引脚定义
#define DS PA0 //串行数据输入
#define SH_CP PA1 //移位时钟脉冲
#define ST_CP PA2 //输出锁存器控制脉冲
#define E1_74HC154 PC7 //74HC154译码器使能
//74595及74154相关引脚操作
#define DS_1() PORTA |= _BV(DS)
#define DS_0() PORTA &=~_BV(DS)
#define SH_CP_1() PORTA |= _BV(SH_CP)
#define SH_CP_0() PORTA &=~_BV(SH_CP)
#define ST_CP_1() PORTA |= _BV(ST_CP)
#define ST_CP_0() PORTA &=~_BV(ST_CP)
//74154译码器使能与禁止
#define EN_74HC154() PORTC &= ~_BV(E1_74HC154)
#define DI_74HC154() PORTC |= _BV(E1_74HC154)
//SPI相关函数
//最多可接收的汉字个数
#define MAX_WORD_COUNT 50
//开始时待显示的中英文字符串
//及从串口接收的中英文数字等字符信息都将覆盖保存到bMsg中
struct MSG
{
INT8U Buffer[MAX_WORD_COUNT*2 + 2];
INT16U Len;
} bMsg;
//缓冲可保存汉字点阵数据的最大汉字个数(如果为半角字符则*2)
#define MAX_DOT_WORD_COUNT 20
//待显示汉字点阵数据缓冲
INT8U WORD_Dots_Buffer[MAX_DOT_WORD_COUNT * 32];
//------------------------------------------------------------------
// USART初始化
//------------------------------------------------------------------
void Init_USART()
{
UCSRB=_BV(RXEN)|_BV(RXCIE); //允许接收,接收中断使能
UCSRC=_BV(URSEL)|_BV(UCSZ1)|_BV(UCSZ0); //8位数据位,1位停止位
UBRRL=(F_CPU/9600/16-1)%256; //波特率:9600
UBRRH=(F_CPU/9600/16-1)/256;
}
//-----------------------------------------------------------------
// 串行输入子程序
//-----------------------------------------------------------------
void Serial_Input_595(INT8U dat)
{
INT8U i;
for(i=0x80;i!=0x00;i>>=1) //由高位到低位,串行输入8位
{
if(dat&i) DS_1();else DS_0();
SH_CP_0();_delay_us(2);
SH_CP_1();_delay_us(2); //移位时钟脉冲上升沿移位
}
}
//-----------------------------------------------------------------
// 并行输出子程序
//-----------------------------------------------------------------
void Parallel_Output_595()
{
ST_CP_0();_delay_us(1);
ST_CP_1();_delay_us(1); //上升沿将数据送到输出锁存器
}
//-----------------------------------------------------------------
// 根据bMsg.Buffer,从硬字库读取全角或半角字符点阵数据并完成必要转换
//-----------------------------------------------------------------
void Read_SPI_Word_Dot_Matrix_AND_Convert()
{
INT16U i,j=0,k;
INT32U offset; //汉字在点阵库中的偏移位置
INT8U SectionCode,PlaceCode; //汉字区码与位码
INT8U Temp_Buf[32]; //转换用临时缓冲
for(i=0;i<MAX_DOT_WORD_COUNT*32;i++) //清空点阵缓存
WORD_Dots_Buffer[i]=0x00;
i=0;
while(i<bMsg.Len)
{
if(bMsg.Buffer[i]>=0xA0) //处理汉字编码
{
//取得汉字区码位
SectionCode=bMsg.Buffer[i]-0xA0;
PlaceCode =bMsg.Buffer[i+1]-0xA0;
//根据当前汉字区位码计算其点阵在字库中的偏移位置
offset=(94L*(SectionCode-1)+(PlaceCode-1))*32L;
//从SPI存储器AT25F4096读取32字节汉字点阵
Read_Some_Bytes_FROM_AT25F4096(offset,Temp_Buf,32);
//汉字字库中点阵格式为16行依次左->右,左->右取字节,为适应点阵屏显示
//下面将其转换为先取左半边16行,再取右半边16行
for(k=0;k<16;k++)
{
WORD_Dots_Buffer[j+k] =Temp_Buf[2*k];
WORD_Dots_Buffer[j+k+16]=Temp_Buf[2*k+1];
}
//每个汉字点阵保存到WORD_Dots_Buffer后跳过32字节
//(每个汉字点阵占32字节)
//从bMsg.Buffer中取字符索引递增2(每个汉字编码占2字节)
j+=32;i+=2;
}
else //处理半角字符编码
{
//ASCII字符偏移地址=ASCII字库在合成字库中的起始位置地址+ASCII码*16
//半角的ASCII字符点阵在合成字库中汉字点阵字库的后面,汉字点阵共计267616字节
offset=267616L+bMsg.Buffer[i]*16;
Read_Some_Bytes_FROM_AT25F4096(offset,WORD_Dots_Buffer+j,16);
//每个半角ASCII字符点阵保存到WORD_Dots_Buffer后跳过16字节
//(每个ASCII点阵占16字节)
//从bMsg.Buffer中取字符索引递增1(每个ASCII编码占1字节)
j+=16;i++;
}
}
}
//-----------------------------------------------------------------
// 主程序
//-----------------------------------------------------------------
int main()
{
INT8U i,j,z,d=0;
DDRA=0xFF;PORTA=0xFF; //配置端口
DDRC=0xFF;PORTC=0xFF;
DDRD=0x02;PORTD=0xFF;
//在显示缓冲中先预设初始时待显示的字符串
strcpy((char*)bMsg.Buffer,"★点阵演示V1.0...★");
bMsg.Len=strlen((char*)bMsg.Buffer);
SPI_MasterInit(); //SPI主机初始化
Init_USART(); //串口初始化
sei(); //接受中断许可
//根据bMsg.Buffer从SPI存储器读取全角或半角字符点阵数据并完成必要的转换
Read_SPI_Word_Dot_Matrix_AND_Convert();
while(1)
{
for(z=0;z<=bMsg.Len-10;z++)
{
for(d=0;d<10;d++) //此循环用于控制显示滚动的速度
{
for(i=0;i<16;i++) //完成每个汉字的16列扫描
{
//数据串行输入595(5块16*16点阵屏,共10片595)
for(j=0;j<5;j++)
{
Serial_Input_595(WORD_Dots_Buffer[z*16+j*32+i+16]);
Serial_Input_595(WORD_Dots_Buffer[z*16+j*32+i]);
}
DI_74HC154(); //先禁用译码器
Parallel_Output_595(); //595数据并行输出
PORTC=(PORTC & 0xF0)|i; //写译码器
EN_74HC154(); //使能译码器,译码输出,选通第i列
_delay_ms(2);
}
}
}
}
}
//-----------------------------------------------------------------
// 串口接收中断函数
//-----------------------------------------------------------------
ISR (USART_RXC_vect)
{
//将当前接收到的字符存入c
INT8U c=UDR;
//接收到 '\r' 时忽略
if(c=='\r')return;
//如果接收到'\n' 表示本次接收完毕
if(c=='\n')
{
//重新从SPI存储器中读取bMsg.Buffer的汉字点阵
Read_SPI_Word_Dot_Matrix_AND_Convert();
return;
}
//缓存新接收的字符
if(bMsg.Len<MAX_WORD_COUNT*2)bMsg.Buffer[bMsg.Len++]=c;
//任何时候接收到“##”时清空缓存
if(bMsg.Len>=2 && bMsg.Buffer[bMsg.Len-1]=='#' && bMsg.Buffer[bMsg.Len-2]=='#')
{
bMsg.Len=0;
}
}
//-----------------------------------------------------------------
// 名称:用SPI接口读写AT25F4096子程序
//-----------------------------------------------------------------
#define F_CPU 4000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/sfr_defs.h>
#include <avr/interrupt.h>
#include <string.h>
#include <stdio.h>
//#define INT8 signed char
#define INT8U unsigned char
#define INT16U unsigned int
#define INT32U unsigned long
//AT25F4096指令集
#define WREN 0x06 //使能写
#define WRDI 0x04 //禁止写
#define RDSR 0x05 //读状态
#define WRSR 0x01 //写状态
#define READ 0x03 //读字节
#define PROGRAM 0x02 //写字节
#define SECTOR_ERASE 0x52 //删除区域数据
#define CHIP_ERASE 0x62 //删除芯片数据
#define RDID 0x15 //读厂商与产品ID
//SPI使能与禁用
#define SPI_EN() (PORTB &= 0xEF)
#define SPI_DI() (PORTB |= 0x10)
//-----------------------------------------------------------------
// SPI主机初始化
//-----------------------------------------------------------------
void SPI_MasterInit()
{
//SPI接口配置
DDRB=0b10110000;PORTB=0xFF;
//SPI使能,主机模式,16分频
SPCR|=_BV(SPE)|_BV(MSTR)|_BV(SPR0);
}
//-----------------------------------------------------------------
// SPI数据传输
//-----------------------------------------------------------------
INT8U SPI_Transmit(INT8U dat)
{
SPDR=dat; //启动数据传输
while(!(SPSR &_BV(SPIF))); //等待结束
SPSR |=_BV(SPIF); //清中断标志
return SPDR;
}
//-----------------------------------------------------------------
// 读AT25F4096芯片状态
//-----------------------------------------------------------------
INT8U Read_SPI_Status()
{
INT8U status;
SPI_EN();
SPI_Transmit(RDSR); //发送读状态指令
status=SPI_Transmit(0xFF);
SPI_DI();
return status;
}
//-----------------------------------------------------------------
// AT25F4096忙等待
//-----------------------------------------------------------------
void Busy_Wait()
{
while(Read_SPI_Status() & 0x01); //忙等待
}
//-----------------------------------------------------------------
// 向AT25F4096写入三个字节的地址0x000000-0x07FFFF (有效位为19位)
//-----------------------------------------------------------------
void Write_3_Bytes_AT25F4096_Address(INT32U addr)
{
SPI_Transmit((INT8U)(addr>>16));
SPI_Transmit((INT8U)(addr>>8));
SPI_Transmit((INT8U)(addr));
}
//-----------------------------------------------------------------
// 从指定地址读单字节
//-----------------------------------------------------------------
INT8U Read_Byte_FROM_AT25F4096(INT32U addr)
{
INT8U dat;
SPI_EN();
SPI_Transmit(READ); //发送读指令
Write_3_Bytes_AT25F4096_Address(addr); //发送3字节地址
dat=SPI_Transmit(0xFF); //读取字节数据
SPI_DI();
return dat;
}
//-----------------------------------------------------------------
// 从指定地址读多字节到缓冲
//-----------------------------------------------------------------
void Read_Some_Bytes_FROM_AT25F4096(INT32U addr, INT8U *p, INT16U len)
{
INT16U i;
SPI_EN();
SPI_Transmit(READ); //发送读指令
Write_3_Bytes_AT25F4096_Address(addr); //发送3字节地址
for(i=0;i<len;i++) //读数据序列
p[i]=SPI_Transmit(0xFF);
SPI_DI();
}
//-----------------------------------------------------------------
// 向AT25F4096指定地址写入单字节数据
//-----------------------------------------------------------------
void Write_Byte_TO_AT25F4096(INT32U addr,INT8U dat)
{
SPI_EN();
SPI_Transmit(WREN); //使能写
SPI_DI();
Busy_Wait();
SPI_EN();
SPI_Transmit(PROGRAM); //写指令
Write_3_Bytes_AT25F4096_Address(addr); //发送3字节地址
SPI_Transmit(dat); //写字节数据
SPI_DI();
Busy_Wait();
}
//-----------------------------------------------------------------
// 名称: 能接收串口信息的带中英文硬字库的80*16点阵显示屏
//-----------------------------------------------------------------
// 说明: 本例运行时,点阵屏将滚动显示一组固定信息
// 当接收到串口发送来的中英文/全角/半角字符时,点屏将开始
// 滚动显示所接收到的信息.
//
//-----------------------------------------------------------------
//74595及74154相关引脚定义
#define DS PA0 //串行数据输入
#define SH_CP PA1 //移位时钟脉冲
#define ST_CP PA2 //输出锁存器控制脉冲
#define E1_74HC154 PC7 //74HC154译码器使能
//74595及74154相关引脚操作
#define DS_1() PORTA |= _BV(DS)
#define DS_0() PORTA &=~_BV(DS)
#define SH_CP_1() PORTA |= _BV(SH_CP)
#define SH_CP_0() PORTA &=~_BV(SH_CP)
#define ST_CP_1() PORTA |= _BV(ST_CP)
#define ST_CP_0() PORTA &=~_BV(ST_CP)
//74154译码器使能与禁止
#define EN_74HC154() PORTC &= ~_BV(E1_74HC154)
#define DI_74HC154() PORTC |= _BV(E1_74HC154)
//SPI相关函数
//最多可接收的汉字个数
#define MAX_WORD_COUNT 50
//开始时待显示的中英文字符串
//及从串口接收的中英文数字等字符信息都将覆盖保存到bMsg中
struct MSG
{
INT8U Buffer[MAX_WORD_COUNT*2 + 2];
INT16U Len;
} bMsg;
//缓冲可保存汉字点阵数据的最大汉字个数(如果为半角字符则*2)
#define MAX_DOT_WORD_COUNT 20
//待显示汉字点阵数据缓冲
INT8U WORD_Dots_Buffer[MAX_DOT_WORD_COUNT * 32];
//------------------------------------------------------------------
// USART初始化
//------------------------------------------------------------------
void Init_USART()
{
UCSRB=_BV(RXEN)|_BV(RXCIE); //允许接收,接收中断使能
UCSRC=_BV(URSEL)|_BV(UCSZ1)|_BV(UCSZ0); //8位数据位,1位停止位
UBRRL=(F_CPU/9600/16-1)%256; //波特率:9600
UBRRH=(F_CPU/9600/16-1)/256;
}
//-----------------------------------------------------------------
// 串行输入子程序
//-----------------------------------------------------------------
void Serial_Input_595(INT8U dat)
{
INT8U i;
for(i=0x80;i!=0x00;i>>=1) //由高位到低位,串行输入8位
{
if(dat&i) DS_1();else DS_0();
SH_CP_0();_delay_us(2);
SH_CP_1();_delay_us(2); //移位时钟脉冲上升沿移位
}
}
//-----------------------------------------------------------------
// 并行输出子程序
//-----------------------------------------------------------------
void Parallel_Output_595()
{
ST_CP_0();_delay_us(1);
ST_CP_1();_delay_us(1); //上升沿将数据送到输出锁存器
}
//-----------------------------------------------------------------
// 根据bMsg.Buffer,从硬字库读取全角或半角字符点阵数据并完成必要转换
//-----------------------------------------------------------------
void Read_SPI_Word_Dot_Matrix_AND_Convert()
{
INT16U i,j=0,k;
INT32U offset; //汉字在点阵库中的偏移位置
INT8U SectionCode,PlaceCode; //汉字区码与位码
INT8U Temp_Buf[32]; //转换用临时缓冲
for(i=0;i<MAX_DOT_WORD_COUNT*32;i++) //清空点阵缓存
WORD_Dots_Buffer[i]=0x00;
i=0;
while(i<bMsg.Len)
{
if(bMsg.Buffer[i]>=0xA0) //处理汉字编码
{
//取得汉字区码位
SectionCode=bMsg.Buffer[i]-0xA0;
PlaceCode =bMsg.Buffer[i+1]-0xA0;
//根据当前汉字区位码计算其点阵在字库中的偏移位置
offset=(94L*(SectionCode-1)+(PlaceCode-1))*32L;
//从SPI存储器AT25F4096读取32字节汉字点阵
Read_Some_Bytes_FROM_AT25F4096(offset,Temp_Buf,32);
//汉字字库中点阵格式为16行依次左->右,左->右取字节,为适应点阵屏显示
//下面将其转换为先取左半边16行,再取右半边16行
for(k=0;k<16;k++)
{
WORD_Dots_Buffer[j+k] =Temp_Buf[2*k];
WORD_Dots_Buffer[j+k+16]=Temp_Buf[2*k+1];
}
//每个汉字点阵保存到WORD_Dots_Buffer后跳过32字节
//(每个汉字点阵占32字节)
//从bMsg.Buffer中取字符索引递增2(每个汉字编码占2字节)
j+=32;i+=2;
}
else //处理半角字符编码
{
//ASCII字符偏移地址=ASCII字库在合成字库中的起始位置地址+ASCII码*16
//半角的ASCII字符点阵在合成字库中汉字点阵字库的后面,汉字点阵共计267616字节
offset=267616L+bMsg.Buffer[i]*16;
Read_Some_Bytes_FROM_AT25F4096(offset,WORD_Dots_Buffer+j,16);
//每个半角ASCII字符点阵保存到WORD_Dots_Buffer后跳过16字节
//(每个ASCII点阵占16字节)
//从bMsg.Buffer中取字符索引递增1(每个ASCII编码占1字节)
j+=16;i++;
}
}
}
//-----------------------------------------------------------------
// 主程序
//-----------------------------------------------------------------
int main()
{
INT8U i,j,z,d=0;
DDRA=0xFF;PORTA=0xFF; //配置端口
DDRC=0xFF;PORTC=0xFF;
DDRD=0x02;PORTD=0xFF;
//在显示缓冲中先预设初始时待显示的字符串
strcpy((char*)bMsg.Buffer,"★点阵演示V1.0...★");
bMsg.Len=strlen((char*)bMsg.Buffer);
SPI_MasterInit(); //SPI主机初始化
Init_USART(); //串口初始化
sei(); //接受中断许可
//根据bMsg.Buffer从SPI存储器读取全角或半角字符点阵数据并完成必要的转换
Read_SPI_Word_Dot_Matrix_AND_Convert();
while(1)
{
for(z=0;z<=bMsg.Len-10;z++)
{
for(d=0;d<10;d++) //此循环用于控制显示滚动的速度
{
for(i=0;i<16;i++) //完成每个汉字的16列扫描
{
//数据串行输入595(5块16*16点阵屏,共10片595)
for(j=0;j<5;j++)
{
Serial_Input_595(WORD_Dots_Buffer[z*16+j*32+i+16]);
Serial_Input_595(WORD_Dots_Buffer[z*16+j*32+i]);
}
DI_74HC154(); //先禁用译码器
Parallel_Output_595(); //595数据并行输出
PORTC=(PORTC & 0xF0)|i; //写译码器
EN_74HC154(); //使能译码器,译码输出,选通第i列
_delay_ms(2);
}
}
}
}
}
//-----------------------------------------------------------------
// 串口接收中断函数
//-----------------------------------------------------------------
ISR (USART_RXC_vect)
{
//将当前接收到的字符存入c
INT8U c=UDR;
//接收到 '\r' 时忽略
if(c=='\r')return;
//如果接收到'\n' 表示本次接收完毕
if(c=='\n')
{
//重新从SPI存储器中读取bMsg.Buffer的汉字点阵
Read_SPI_Word_Dot_Matrix_AND_Convert();
return;
}
//缓存新接收的字符
if(bMsg.Len<MAX_WORD_COUNT*2)bMsg.Buffer[bMsg.Len++]=c;
//任何时候接收到“##”时清空缓存
if(bMsg.Len>=2 && bMsg.Buffer[bMsg.Len-1]=='#' && bMsg.Buffer[bMsg.Len-2]=='#')
{
bMsg.Len=0;
}
}