原创 AVR硬件SPI从机模式调试记录!

2009-5-15 17:56 6124 4 4 分类: MCU/ 嵌入式

由于第一次用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();//喂狗
  }


}

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
4
关闭 站长推荐上一条 /3 下一条