由于使用到的DA芯片使用的是spi接口的通信,所以着重研究了一下SPI模块。G2系列的串行通信模块即可以配置成SPI的主或从方式,又可以配置成IIC模式,不需要使用软件模拟时序,大大方便了开发。
SPI通信,SPI通信属于同步通信。即发送者和接受者使用同一个时钟,在相同时钟的驱动下工作,与常见的串口通信有所区别。考虑到DA的时序,时钟的频率需要在1Mhz一下,DA在下降沿采集数据,数据线空闲时置低。由于MSP430只需要发送数据,可不需要输入的数据线。
顺着左下角的紫色箭头浏览SPI的时钟线。首先是选择SPI的时钟源。USISELx 寄存器用于选择SPI时钟的来源。可以选择辅助时钟、系统子时钟、定时器模块产生的时钟等。USIDIVx用于选择时钟分频的系数,用于获得需要的时钟频率。USIMST寄存器用于选择430单片机作为SPI的主设备或者从设备。在此设计中430 用作主设备,向外提供时钟。时钟一路向右,为从设备提供时钟,;另一路向左为430的SPI模块提供时钟。寄存器USICKPH 和USICKPL用于选择时钟的相位和决定在上升沿或者下降沿采样。
USISR寄存器由两个8bit寄存器组成,用于存放SPI的收发数据。USICNTx 模块是计数模块,控制要发送的数据的位数。当USISWRST置位的时候,可以时能数据模块和计数模块,spi通信就开始工作了。
配合DA分析一下SPI寄存器模块的设计。下面是TLV5620的时序图。
每一次模式转换需要11位数据。其中A1,A0 用于选择使用哪个通道。一共有4路DA通道,由于仅用到A通道,前两位为00。
RNG位用于选择倍数,这里最大输出选择的参考电压的一倍,此位设置为0即可。D7,D6,D5,D4,D3,D2,D1,D0为8位数据用于控制输出电压,先发送最高有效位。数据线上一次传送11位数据,因此计数器中存入11,USI16B置高后,数据存储单元最多可以存放16位数据。USILSB用于选择先发送最高有效位。 LOAD这个线需要在11个时钟过后发送一个低脉冲,通知DA更新DA输出。至此,一帧数据完成发送。
橘黄色的线指示中断信号,USICNTx中的数在每一个时钟沿后减一,减到0后触发中断。中断信号送到分频器模块,停止寄存器的输出。当需要再次发送数据的时候,向USICNTx写入传送位数,即可清除中断信号。
程序的流程大体是 1)配置好SPI模块的各个寄存器的数据,使得其能够工作在需要的模式。 2)使能计数器和移位器模块,使得SPI开始工作 3)进入低功耗的模式 4)传送完成后引发中断 5)先装载传送数据,再存入发送数据个数启动下一次的数据传送
作为信号源加入一级跟随器,使得DA的输出具有驱动能力,同时避免负载对输出端口的影响。跟随级后面添加有源或无源的巴特沃斯滤波器,滤除信号以外频率的信号。作为一个练手的东西,就不做了。输出数据可以用matlab生成。
------------------------------------------------------------- 实物图片 -------------------------------------------------------------
该图为使用面包板搭建的电路,可以方便的验证电路。
该图为信号源输出的正弦波
输出波形的FFT变换,可以看到有两个峰值-------------直流成分和正弦波成分。
---------------------------------------------------程序部分-----------------------------------------------------------------------------------
#include<msp430g2231.h>
#include"source.h"
#define DAC_UPDATE BIT4
unsigned char i=0;
void update_spi(unsigned char i);
#define debug
#ifdef debug
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; //关闭看门狗
BCSCTL1 = CALBC1_1MHZ; // Set range
DCOCTL = CALDCO_1MHZ; // Set DCO step + modulation */
P1DIR |= DAC_UPDATE; //设置成为输出
P1OUT |= DAC_UPDATE; //输出高电平
P1REN |= DAC_UPDATE; // 输出口上拉电阻
USICKCTL =USISSEL_2; // 16 SMCLK 选择时钟源为SMLK
USICKCTL &= ~USICKPL;
USICTL0 &= ~USILSB; //清零从高位开始发送
//BCSCTL3 |= LFXT1S_2; // ACLK = VLO
USICTL1 &=~(USII2C+USICKPH); //关闭I2C模式,下降沿发送数据
USICNT |= USI16B; //允许使用高八位
USICTL1 |= USIIE; // Counter interrupt, flag remains set
USICTL0 |= USIPE5 + USIPE6 + USIMST + USIOE; // USIPE6 SDO打开,输出 USIPE7 SDI打开,输入 USIPE8 SCLK 时钟输出 设置为主模式
USICTL0 &= ~USISWRST; // USI released for operation
_BIS_SR(LPM0_bits + GIE); // Enter LPM0 w/ interrupt
while(1)
{
}
}
#pragma vector=USI_VECTOR
__interrupt void universal_serial_interface(void)
{
//USICNT |=USIIFGCC;
//P1OUT ^= 0x01; // Toggle P1.0 using exclusive-OR
P1DIR |=0x10;//DAC_UPDATE; //设置成为输出
P1OUT &= ~DAC_UPDATE;
if(i>=199)i=0;
else i++;
update_spi(i);
//USISRH = 0x07;
//USISRL = 0x54;
//P1OUT = 0x10; // P1.4 set, else reset
//P1REN |= 0x10; // P1.4 pullup
}
void update_spi(unsigned char i)
{
USISRH = digital_signal >>3;
USISRL = (digital_signal & 0x07)<<5;
P1OUT |= DAC_UPDATE;
USICNT |= 0x0B; // re-load counter
}
#endif
#ifndef debug
void main()
{
WDTCTL = WDTPW + WDTHOLD; //关闭看门狗
P1DIR |=0x10;//DAC_UPDATE; //设置成为输出
P1OUT |=0x10;//DAC_UPDATE; //输出高电平
while(1);
}
#endif
|