原创 AVR学习日志(五十四)—学习使用SPI串行通信

2010-12-18 00:23 6288 9 10 分类: MCU/ 嵌入式


         这是前两天写的关于SPI的测试函数,先简单的复习一下SPI的全称是Serial Peripheral Interface,即串行外围接口,它是Motorola公司推出的一种同步串行通讯方式,是一种四线同步总线。SPI接口的器件包括FLASHRAM、网络控制器、LCD显示驱动器、A/D转换器和MCU等。SPI总线系统可直接与各个厂家生产的多种标准外围器件直接接口,SPI接口一般使用4条线:串行时钟线(SCLK)、主机输入/从机输出数据线MISO、主机输出/从机输入数据线MOSI和低电平有效的从机选择线SS(有的SPI接口芯片带有中断信号线INT、有的SPI接口芯片没有主机输出/从机输入数据线MOSI)。 SPI接口是在CPU和外围低速器件之间进行同步串行数据传输,在主器件的移位脉冲下,数据按位传输,高位在前,低位在后,为全双工通信,数据传输速度总体来说比I2C总线要快,速度可达到几Mbps,具体速度大小取决于SPI接口的硬件。


    SPI接口是以主从方式工作的,这种模式通常有一个主器件和一个或多个从器件,懒猫以前曾用过SPI的器件,但都用用单片机的通用IO口模拟的,没有真正的用过单片机自带的SPI,模拟的SPI虽然能传输数据,但是时钟可能不是太精确,所以速度高时可能会丢失数据。今天懒猫就试着用一下单片自带的SPI做一下实验。


    懒猫先的SPI器件是美国微芯公司生产带SPI接口的数字温度传感器,它达到10 位的分辨率(0.25℃/位),工作电压在2.65V到5.5V之间,功耗也不是很高连续温度转换模式下,消耗的电流是250微安,在判断模式下消耗的电流约为1微安。具体的参数可以参看TC72的数据手册。本次实验的仿真电路很简单,TC72的SPI接口与单片机的SPI接口相连,单片机的串口输出再接一个虚拟终端,用于显示测量的温度值。软件仿真电路由Proteus7.4所作,只有7.4或以上版本才能打开仿真电路,仅在调试程序时辅助使用,不能用于实际设计,具体设计请详细阅读芯片数据手册。软件仿真电路如下所示:


4a9943ea-592b-4cc8-9dfe-e74fc57b519c.jpg


图1-1 电路原理图


      简单说一下软件部分,软件分三个文件,第一个是main.c包括主要的处理函数,第二个是SPI_TC72.h主要是TC72的一些驱动函数,第三个是usart.h它里面包括串口发送与接收的一些函数。在main.c中用到sprintf函数,它是一个变参函数,包含在stdio.h这个头文件中,它的函数原型是int sprintf( char *buffer, const char *format [, argument] … ); 返回值是字符串长度(strlen)。关于这个函数的具体应用可以查阅相关资料,也可以上网上查找,因为这个函数可能导致溢出错误,所以网上能搜出一大堆关于这个函数的话题,这里懒猫就不多啰嗦了。下面贴出测试程序:


一、main.c


/**********************************************************************************


//文件名称:main.c


//功    能:学习使用SPI接口的温度传感器TC72


//作    者:懒猫爱飞


//建立日期:2010.12.14


//备    注:MCU-ATmega8515 开发环境-winavr100110  Crystal-8MHz


***********************************************************************************/


#include <avr/io.h>


#include <util/delay.h>


#include <inttypes.h> //关于数据类型的一些定义


#include <stdint.h>    //同样是一些数据类型的定义


#include <math.h>


#include <stdio.h>


#include "usart.h"


#include "SPI_TC72.h"


 


//the macro about SPI enable or disable


#define SPI_EN()         PORTB &= ~_BV(PB4)


#define SPI_DIS()   PORTB |= _BV(PB4)


 


/****************************************************************


//函数名称:Port_init(void)


//功    能:端口初始化


//入口参数:无


//出口参数:无


*****************************************************************/


void Port_init(void)


{


              DDRA  = 0xff;                                         //A口输出口


              PORTA = 0x00;


             


              DDRB  = 0xff;                      //B口输出


              PORTB = 0x00;


             


              DDRC  = 0x00;                                        //C口输入


              PORTC = 0x00;


             


              DDRD  = 0xff;                                         //D口输出


              PORTD = 0x00;


}


 


/**********************************************************************************


//函数名称:int main(void)


//功    能:主函数


//入口参数:NULL


//出口参数:NULL


//备    注:NULL


***********************************************************************************/


int main(void)


{


    int d,f;


              float cur_temp = 0.00;


              float pre_temp = 0.00;


     unsigned char T[2];


              char dis_buf[50];


 


    Port_init();


              usart_init();


    spi_mast_init();


 


              sprintf(dis_buf,"By : LanMaoAiFei ^_^\r\n2010-12-15 night \r\n\r\n");


              send_string(dis_buf);


 


              config_TC72();         //配置TC72


              _delay_ms(200);


 


    //读取并显示制造商ID


              sprintf(dis_buf,"Manufacture ID is : 0x%02X\r\n",read_TC72_ID());


              send_string(dis_buf);


              //提示开始读取温度值


              send_string("Begin read temperature from TC72...\r\n");


              while(1)


              {


                            read_tc72_temp(T);             //第一次读取数据


        cur_temp = convert_data(T);    //格式转换


                            read_tc72_temp(T);             //再一次读取数据


 


        if(cur_temp != convert_data(T))


                            {


                                          continue;                  //如果连续两次读取的温度不一致则继续


                            }


        if(cur_temp == pre_temp)    


                            {


                                          continue;                  //如果温度值未变化,继续采集


                            }


 


        pre_temp = cur_temp;           //否则更新当前值


 


        d = (int)cur_temp;             //整数部分


                            f = (int)(fabs(cur_temp - d)*100);    //小数部分


 


        //发送数值


                            sprintf(dis_buf,"T[0]:0x%02X  T[1]:0x%02X --> %3d.%2d‘C \r\n",T[0],T[1],d,f);


                  send_string(dis_buf);         //串口输出采集结果


              }


 


              return 0;


}


 


二、 SPI_TC72


/**********************************************************************************


//文件名称:SPI_TC72.h


//功    能:关于传感器的一些函数及一些宏定义


//作    者:懒猫爱飞


//建立日期:2010.12.14


//备    注:MCU-ATmega8515 开发环境-winavr100110  Crystal-8MHz


***********************************************************************************/


#ifndef __SPI_TC72_H__


#define __SPI_TC72_H__


 


#include <avr/io.h>


#include <util/delay.h>


 


//SPI使能与禁止(因为TC72是高电平使能,低电平禁用,所以注意宏定义)


#define TC72_EN()    PORTB |= _BV(PB4)


#define TC72_DIS()   PORTB &= ~_BV(PB4)


 


 


//TC72寄存器地址定义


#define TC72_CTRL                  0x80     //控制寄存器


#define TC72_TEMP_LSB      0x01     //温度低字节


#define TC72_TEMP_MSB    0x02     //温度高字节


#define TC72_MANU_ID       0x03     //制造商ID


 


/**********************************************************************************


//函数名称:void spi_mast_init(void)


//功    能:SPI主控模式初始化函数


//入口参数:NULL


//出口参数:NULL


//备    注:NULL


***********************************************************************************/


void spi_mast_init(void)


{


              DDRB  = 0B10110000;                                                    //PB4 - OUT, PB5 - OUT, PB6 - IN, PB7 - OUT


              PORTB = 0xFF;


 


              SPCR |= _BV(SPE)|_BV(MSTR)|_BV(SPR0);            //使能SPI,


}


 


/**********************************************************************************


//函数名称:int8_t spi_transmit(int8_t dat)


//功    能:SPI数据传输函数


//入口参数:dat - 要传送的数据


//出口参数:NULL


//备    注:NULL


***********************************************************************************/


unsigned char spi_transmit(unsigned char dat)


{


              SPDR = dat;                   //发送数据


              while(!(SPSR & _BV(SPIF)));   //等待结束


              SPSR |= _BV(SPIF);            //清中断标志


              return SPDR;


}


 


/**********************************************************************************


//函数名称:void write_TC72_dat(unsigned char addr,unsigned char dat)


//功    能:向TC72指定地址写入一个数据


//入口参数:dat - 要传送的数据    addr - 寄存器地址


//出口参数:NULL


//备    注:NULL


***********************************************************************************/


void write_TC72_dat(unsigned char addr,unsigned char dat)


{


              TC72_EN();


    spi_transmit(addr);    //写地址


              spi_transmit(dat);     //写入数据


              TC72_DIS();


}


 


/**********************************************************************************


//函数名称:void config_TC72(void)


//功    能:配置TC72


//入口参数:NULL


//出口参数:NULL


//备    注:NULL


***********************************************************************************/


void config_TC72(void)


{


              write_TC72_dat(TC72_CTRL,0x15);    //配置为单次转换与判断模式          


}


 


/**********************************************************************************


//函数名称:unsigned char read_TC72_ID(void)


//功    能:读TC72制造商ID


//入口参数:NULL


//出口参数:ID


//备    注:NULL


***********************************************************************************/


unsigned char read_TC72_ID(void)


{


              unsigned char ID;


 


              TC72_EN();


              spi_transmit(TC72_MANU_ID);


              ID = spi_transmit(0xFF);


              TC72_DIS();


 


              return ID;


}


/**********************************************************************************


//函数名称:void read_tc72_temp(unsigned char t[])


//功    能:从TC72中读取2字节温度数据


//入口参数:NULL


//出口参数:t[0] t[1]


//备    注:NULL


***********************************************************************************/


void read_tc72_temp(unsigned char t[])


{


              config_TC72();    //配置TC72


              _delay_ms(200);


              TC72_EN();


    spi_transmit(TC72_TEMP_MSB);    //发送读取温度高字节命令


    t[1] = spi_transmit(0xFF);      //读取高位


              t[0] = spi_transmit(0xFF);      //读取低位


    TC72_DIS();


}


 


/**********************************************************************************


//函数名称:float convert_data(unsigned char t[])


//功    能:将读取的2字节温度数据转换为十进制温度


//入口参数:NULL


//出口参数:t[0] t[1]


//备    注:NULL


***********************************************************************************/


float convert_data(unsigned char t[])


{


              float TempX = 0.0;        //浮点温度值


              char sign = 1;         //正负标识,默认为正数


 


              t[1] <<= 1;               //先左移一位,再处理


 


              if(t[0] & 0x80)


              {


                            t[1] |= 0x01;


              }


 


              t[0] <<= 1;              //先左移一位,再处理


 


              if(t[1] & 0x80)          //如果高位为负,将两字节取反加1


              {


                            t[1] = ~t[1];        //t[1] 取反(高位取反)


                            t[0] = ~t[0]+1;      //t[0] 取反加1(代位)


                           


                            if(t[0] == 0x00)


                            {


                                          t[1] += 0x01;   //如果t[0]取反加1后为0x00刚进位


                            }


                            sign = -1;          //设“-”符号


              }


 


              TempX = (float)t[1];   //得到整数部分


             


              if(t[0]&0x80)          //分别累加两位小数部分


              {


                            TempX += 0.5;


              }


    if(t[0]&0x40)


              {


                            TempX += 0.25;


              }


 


              return sign*TempX;      //返回带符号的温度值


}


 


#endif


三、Usart.h


/**********************************************************************************


//文件名称:usart.h


//功    能:关于串口的一些函数


//作    者:懒猫爱飞


//建立日期:2010.12.14


//备    注:MCU-ATmega8515 开发环境-winavr100110  Crystal-8MHz


***********************************************************************************/


#ifndef __USART_H__


#define __USART_H__


 


#include <avr/io.h>


 


#define F_CPU 8000000UL    //8MHz 晶振


 


#define uint  unsigned int


#define uchar unsigned char


 


 


/**********************************************************************************


//函数名称:void usart_init(void)


//功    能:串口初始化


//入口参数:NULL


//出口参数:NULL


//备    注:NULL


***********************************************************************************/


void usart_init(void)


{


              UCSRB |= _BV(RXEN)|_BV(TXEN);                                             //允许接收与发送


              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 send_byte(uchar C)


//功    能:发送字符


//入口参数:C


//出口参数:无


*****************************************************************/


void send_byte(uchar C)


{


 


 while(!(UCSRA&(1<<UDRE)));    //wait for empty


 UDR=C;                        // send the data


 


}


/****************************************************************


//函数名称:void Sendstring(uchar *pd)


//功    能:发送字符串


//入口参数: *pd


//出口参数:无


*****************************************************************/


void send_string(uchar *pd)


{


  while((*pd)!='\0') //发送字符串,直到遇到0才结束


  {


   send_byte(*pd); //发送一个字符


   pd++;  //移动到下一个字符


  }


}


 


const uchar HexTable[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};


/****************************************************************


//函数名称:void SendShortIntHex(uint x)


//功    能:以十六进制形式发送整数x


//入口参数:x


//出口参数:无


*****************************************************************/


void SendShortIntHex(uint x)


{


 uchar i;


 uchar display_buffer[7];


 display_buffer[6]=0;


 display_buffer[0]='0';


 display_buffer[1]='x';


 for(i=5;i>=2;i--) //将整数转换为4个字节的HEX值


  {


   display_buffer=HexTable[(x&0xf)];


   x>>=4;


  }


 send_string(display_buffer);


}


 


#endif


 


         程序不是太难,但在实际应用时注意速率与时钟模式,不同的器件可能需要不同的时钟模式,设计时请仔细阅读数据手册,懒猫别的不多说了,哦,忘了,这几天深圳这边有点冷,大家注意保暖^_^懒猫该继续修行去了……


  


      最后,再喊一下口号,鼓励一下自己:


 


每天进步一点点,开心多一点^_^


 

PARTNER CONTENT

文章评论1条评论)

登录后参与讨论

用户377235 2013-8-25 21:25

你这个调试过没有 有很明显的错误啊
相关推荐阅读
xucun915_925777961 2013-04-28 11:31
毕业后的五年拉开大家差距的原因在哪里?【转】
  有人工作,有人继续上学,大家千万不要错过这篇文章,能看到这篇文章也是一种幸运,真的受益匪浅,对我有很大启迪,这篇文章将会改变我的一生,真的太好了,希望与有缘人分享,也希望对有缘人有所帮助!...
xucun915_925777961 2013-03-31 20:28
职场大牛精彩总结:职场上,如何做人做事做管理【转】(文/张子阳)
      大道至简,越是根源和基本的问题,道理实际上越简单。关于如何做人、做事、做管理的书很多,我看得不多,但是我觉得这些书更多是侧重技术和实现细节上的,而很少从人的思想和观念去讲。实际...
xucun915_925777961 2013-03-28 14:03
介绍几种优秀的UML工具
统一建模语言(UML是 Unified Modeling Language的缩写)是用来对软件密集系统进行可视化建模的一种语言。UML为面向对象开发系统的产品进行说明、可视化、和编制文档的一种标准语言...
xucun915_925777961 2013-03-27 18:05
IAR EW FOR 8051 修改工程名称的方法
        近段时间一直在搞蓝牙4.0的开发任务,所以用到了IAR编译器,话说IAR编译器用途蛮广,可以编译MSP430,ARM,还有TI的51核芯片,当然,这要不同的版本,我现在用的是for...
xucun915_925777961 2013-03-26 16:40
Cortex-M3学习日志(八)-- TFT实验Part(a)
    自己用的LPC1768的开板带有一块2.4寸的TFT彩色液晶屏,虽然自己工作中还用不过显示屏,但是晚上闲着也是闲着,于是继续玩咱的LPC1768吧。 我的开发板上的液晶屏的用的是SPI接口,当...
xucun915_925777961 2013-03-25 20:48
说说那几款查看源代码的工具
说说那几款查看代码的工具 今天给大家介绍几款单片机工程师所喜欢的几款查看源代码的工具,这几款工具在懒猫的日常工作中可是帮了不少忙。 一、 Source Insight     Source Insig...
EE直播间
更多
我要评论
1
9
关闭 站长推荐上一条 /1 下一条