这是前两天写的关于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或以上版本才能打开仿真电路,仅在调试程序时辅助使用,不能用于实际设计,具体设计请详细阅读芯片数据手册。软件仿真电路如下所示:
图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
程序不是太难,但在实际应用时注意速率与时钟模式,不同的器件可能需要不同的时钟模式,设计时请仔细阅读数据手册,懒猫别的不多说了,哦,忘了,这几天深圳这边有点冷,大家注意保暖^_^懒猫该继续修行去了……
最后,再喊一下口号,鼓励一下自己:
每天进步一点点,开心多一点^_^
用户377235 2013-8-25 21:25