由于第一次用MEGA8的SPI从模式,程序使用中断模式接收,刚开始spi不能进入中断,网上查资料,
有网友把MOSI方向设为输出能解决问题,按照此方法果然进入了中断,程序顺利完成并打开看门狗。
试着把MOSI又改为输入,程序运行正常,有待进一步验证。
uart.h
extern void uart_init(void);
extern void send_char(unsigned char c);
extern void send_string(unsigned char * string , unsigned int strLen);
uart.c
#include<avr/io.h>
#include <avr/wdt.h>
#include<util/delay.h>
#include<avr/eeprom.h>
#include<avr/pgmspace.h>
#include<avr/interrupt.h>
#include "uart.h"
/************************************************************
* 函数名称:void uart_init()
* 功 能:串口初始化
* 入口参数:无
* 出口参数:无
************************************************************/
void uart_init(void)
{
UCSRB = 0x00; //disable while setting baud rate
UCSRA = 0x00;
UCSRC = (1<<URSEL) | 0x06;//异步,8位数据,无奇偶校验,一个停止位,无倍速
/*
UBRRH与UCSRC共用I/O 地址。因此访问该地址时需注意以下问题。
写访问
当在该地址执行写访问时, USART 寄存器选择位(URSEL)控制被写入的寄存器。
若URSEL为0,对UBRRH值更新;若URSEL为1,对UCSRC设置更新
读访问
对UBRRH 或UCSRC 寄存器的读访问则较为复杂。但在大多数应用中,基本不需要读这些寄存器
没有UBRR这个16位寄存器,因为UBRRL(0x09)/UBRRH(0x20)的地址不连续,而且UBRRH跟UCSRC共用地址
U2X=0时的公式计算
UBRRL= (F_CPU/BAUDRATE/16-1)%256;
UBRRH= (F_CPU/BAUDRATE/16-1)/256;
U2X=1时的公式计算
UBRRL= (F_CPU/BAUDRATE/8-1)%256;
UBRRH= (F_CPU/BAUDRATE/8-1)/256;
*/
/*------也可根据数据手册的[波特率设置的例子]查得---------*/
UBRRL = 0x2F; //9600
UBRRH = 0x00; //set baud rate hi
UCSRB = 0x18; //使能接收,使能发送
}
/************************************************************
* 函数名称:void send_char()
* 功 能:串口发送单字符
* 入口参数:unsigned char c
* 出口参数:无
************************************************************/
void send_char(unsigned char c)
{
while( !(UCSRA & (1<<UDRE)) );
UDR=c;
}
/************************************************************
* 函数名称:void send_string()
* 功 能:串口发送字符串
* 入口参数:unsigned char * string , unsigned int strLen
* 出口参数:无
************************************************************/
void send_string(unsigned char * string , unsigned int strLen)
{
unsigned int i="0";
do
{
send_char( *(string +i) );
i++;
}while( i < strLen );
}
主函数
/*****************************************************************************
* 项 目: 基于科大讯飞XF-S4040语音播放小系统
* 功 能: 实现SPI转UART(spi从机模式)
* 目标系统: ATmega8L-8AU
* 时 钟: 7.3728MHz
* 开发软件: AVR Studio4.14
* 版 本: Version 1.0
* 原版时间: 2009-05-13
* 修改时间:
* 说 明:
* 开发人员: zjf
* 说 明: spi从模式MOSI刚开始设置为输入时未发生spi中断,设置为输出中断发生,有待进一步验证!
进一步测试,设置为输入也可以进入中断,奇怪!!!
*****************************************************************************/
#include<avr/io.h>
#include <avr/wdt.h>
#include<util/delay.h>
#include<avr/eeprom.h>
#include<avr/pgmspace.h>
#include<avr/interrupt.h>
#include<avr/signal.h>
#include"uart.h"
#define CHECK_4040 (PINC&0x02) //判S4040状态,1=忙,0=就绪
#define BUSY_4040 (PORTC=PORTC|0x01) //输出S4040忙信号
#define READY_4040 (PORTC=PORTC&0xFE) //输出S4040就绪信号
/*----------------SPI端口定义-----------------*/
#define SPI_CS_DDR DDRB
#define SPI_CS_PORT PORTB
#define SPI_CS_BIT 2
#define SPI_SCK_DDR DDRB
#define SPI_SCK_PORT PORTB
#define SPI_SCK_BIT 5
#define SPI_MOSI_DDR DDRB
#define SPI_MOSI_PORT PORTB
#define SPI_MOSI_BIT 3
#define SPI_MISO_DDR DDRB
#define SPI_MISO_PORT PORTB
#define SPI_MISO_BIT 4
#define SPI_PORT_INI {\
SPI_CS_PORT |= 1<<SPI_CS_BIT;\
SPI_SCK_PORT |= 1<<SPI_SCK_BIT;\
SPI_MOSI_PORT |= 1<<SPI_MOSI_BIT;\
SPI_MISO_PORT |= 1<<SPI_MISO_BIT;\
\
SPI_CS_DDR &= ~(1<<SPI_CS_BIT);\
SPI_SCK_DDR &= ~(1<<SPI_SCK_BIT);\
SPI_MOSI_DDR &= ~(1<<SPI_MOSI_BIT);\
SPI_MISO_DDR |= 1<<SPI_MISO_BIT;\
}
unsigned char spi_buf[850]={0,0,0,0,0,0,0,0,0};//语音信息存储区
volatile unsigned char spi_ok=0;
unsigned int spi_rxd_count=0;
unsigned int data_length=0;
unsigned char is_head=0;
unsigned char tmp="0";
//格式:帧头(1字节)数据区长度(2字节)数据区(包括命令字 文本编码格式 待合成文本)
//测试音“科大讯飞”
//unsigned char com_buf[13]={0XFD,0X00,0X0A,0X01,0X00,0XBF,0XC6,0XB4,0XF3,0XD1,0XB6,0XB7,0XC9};
/************************************************************
* 函数名称:void SPI_Low()
* 功 能:SPI初始化为低速模式
* 入口参数:无
* 出口参数:无
************************************************************/
void SPI_Low(void)
{
SPCR &= ~_BV(DORD);
SPCR = _BV(SPE)|_BV(MSTR)|_BV(SPR0);
SPSR &= ~_BV(SPI2X);
}
/************************************************************
* 函数名称:void SPI_High()
* 功 能:SPI初始化为高速模式
* 入口参数:无
* 出口参数:无
************************************************************/
void SPI_High(void)
{
//从模式下速率取决于主机
//SPCR &= ~_BV(MSTR);//从模式,默认为从机,不用重配置
SPCR = _BV(SPE)|_BV(SPIE);/* 使能 SPI,中断允许 */
// SPSR |= _BV(SPI2X);
}
/************************************************************
* 函数名称:void SPI_Init()
* 功 能:SPI初始化
* 入口参数:无
* 出口参数:无
************************************************************/
void SPI_Init(void)
{
SPI_High();
SPI_PORT_INI; /* Spi Initialize */
SPSR = 0x00;
tmp = SPSR;
tmp = SPDR; //清空SPI,和中断标志,使SPI空闲
}
/************************************************************
* 函数名称: void port_init(void)
* 功 能:端口初始化
* 入口参数:无
* 出口参数:无
************************************************************/
void port_init(void)
{
PORTC=0xfe;//1111 1110 PC0上电输出4040为就绪状态
DDRC=0xfd;//1111 1101 PC1检测4040状态,配置为输入; PC0输出4040状态
PORTD = 0x00;//无上拉
DDRD = 0x00;//输入
}
/************************************************************
* 函数名称:init_devices()
* 功 能:系统初始化
* 入口参数:无
* 出口参数:无
************************************************************/
void init_devices(void)
{
//stop errant interrupts until set up
cli(); //disable all interrupts
port_init();
uart_init();
SPI_Init();
MCUCR = 0x00;
GICR = 0x00;
TIMSK = 0x00; //timer interrupt sources
sei(); //re-enable interrupts
//all peripherals are now initialized
}
/************************************************************
* 函数名称:SIGNAL(SIG_SPI)
* 功 能:spi中断
* 入口参数:无
* 出口参数:无
************************************************************/
SIGNAL(SIG_SPI)//spi中断
{
tmp="SPDR";
if(is_head==0)
{
if(tmp==0xfd) //判断帧头
is_head=1;
}
if(is_head==1)
{
spi_buf[spi_rxd_count]=tmp; //存入spi数据存储区
spi_rxd_count++; //spi接收数据计数加1
}
if(spi_rxd_count==3) //如果接收到了3个字节,则计算数据长度,共两字节,高字节在前,低字节在后
{
data_length=spi_buf[1];
data_length=(data_length<<8)|spi_buf[2];
}
if(spi_rxd_count==(data_length+3))//判断一帧数据是否结束
{
spi_ok=1;
}
}
/************************************************************
* 函数名称:int main()
* 功 能:主函数
* 入口参数:无
* 出口参数:无
************************************************************/
int main()
{
init_devices();
//_delay_ms(1000); _delay_ms(1000); _delay_ms(1000);
//send_string(com_buf , 13); //测试音“科大讯飞”
wdt_enable(WDTO_1S); //看门狗初始化为1秒
wdt_reset();//喂狗
while(1)
{
if(spi_ok)
{
cli();//关中断
BUSY_4040;//忙
_delay_ms(10);
wdt_reset();//喂狗
send_string(spi_buf ,spi_rxd_count ); //向语音芯片发送信息
wdt_reset();//喂狗
spi_ok=0;
spi_rxd_count=0;
data_length=0;
is_head=0;
_delay_ms(10);
sei();//开中断
}
if(CHECK_4040==0) //检测语音芯片状态
{
READY_4040; //就绪
}
else BUSY_4040;//忙
wdt_reset();//喂狗
}
}
文章评论(0条评论)
登录后参与讨论