【Mini-F3575-OB开发板测评】硬件SPI驱动DAC11001A生成正弦波

前言:

在高精度DAC领域,有很多的DAC产生器可以供我们去选择,在这里,我们使用的是DAC11001A这款芯片来产生高精度的DAC波形,使用Mini-F3570开发板自带的硬件SPI1来对这给芯片做出控制。

DAC11001A评估版介绍:

DAC11001A是德州仪器推出的一款高精度的DAC产生器,可以产生很高精度的DAC电压值,所以在很多对精度高的场合被广泛的使用,他使用两个标准基准电压来确定其最大输出电压范围,这里使用正负5V作为其输出的电压基准。
评估板上搭载了一颗DAC11001A芯片,外围电路包括一个输出电压缓冲器,二阶滤波器,和一个基准电压提供模组,由此构成了这个评估板的整体架构,可以通过SPI接口来发送控制命令,控制的寄存器多达六个,有很多专业的位可以用于配置DAC11001A,这里介绍一下需要用到的寄存器和配置SPI的方法
 title=

用到的寄存器:

在这个芯片,用到了其中的数据寄存器,配置寄存器1和和配置寄存器4和一个状态寄存器:
数据寄存器是存放DAC数据的寄存器,把数据存入数据寄存器以后,通过LDAC控制数据的加载,当数据加载到输出端口,可以选择使用电压跟随器直接输出,还是二阶滤波器或者一阶滤波器输出数据,这样就可以控制DAC信号的大小和限幅,由于TI的DAC11001A主打的是20位高精度,低噪声,低纹波的DAC发生器,所以用来对一些条件苛刻的场合有很好的应用,这里介绍一下这里需要用到的寄存器:

数据寄存器:

存放20位DAC数值的DAC数值的寄存器,当外部输出引脚LDAC被低电平使能以后,在DAC输出端口输出寄存器里对应的电平,这就是通过数据寄存器来发送数据的方法,注意,第一个8位,数据存的是地址和读写标志位,后面4~32位是左对齐的20位DAC数据,当然,数据的位数只要符合左对齐,只会影响其精度,不会对其输出的电压值有影响。
需要注意读写标志位,有的位是只读的,有些位是可读可写的。
image.png

配置寄存器2:

配置寄存器2主要用到的位有这两个:
ENALMP:开启警报功能,警报功能指的是DAC采样的温度校准功能,当温度校准结束以后,报警位被开启,在寄存器里会有相应的位被置位,反应DAC温度校准的情况,并且如果开启报警输出功能,报警输出会反映到ALARM引脚上,引脚低电平激活,表明了温度校准结束,反之,这个引脚可能是开漏输出模式,他的引脚在评估板上有一个电阻拉高这个引脚,只能输出低电平,这样就可以把内部温度校准状态反映到外部引脚上,我们随之可以看到温度校准的状态,但是可惜的是,ALARM引脚仅仅引出了一个焊盘,我不想破坏DAC评估板的完整性,所以就不焊接了,我们去读取寄存器里的报警位来判断温度校准状态。
这里我们使用第12位,开启温度校准功能,手册上说着,这个位是默认不开启的,所以想启用温度校准功能需要我们手动开启这个功能,TI设计默认不开启还是考虑到了功耗的问题,低功耗是不需要开启这个引脚的,温度校准在一个恒定温度条件下,没必要频繁开启温度校准。
第9~6位同样需要去使用,这个位设置的是校准电压的范围和输出的电压范围,由于板子上自带一个稳压电平芯片,可输出一个标准的5V电压信号,通过一个电压跟随器和一个反相器取反以后成为输入到DAC11001A的基准电压,然后DAC输出的电压就在这两个参考电压之间输出,跟电源没关系,而且需要注意的是,DAC的VCC输入端的电压需要在6V以上,否则会让芯片处于低电压复位状态,这里采用+-9V作为其电源输入电压,这样可以让芯片处于一个正常的工作状态,由于参考电压是+-5V,所以输出电压处于+-5V之间输出,经过一个电压跟随器可以提高其带载能力,从而输出能力提高。
image.png

状态寄存器:

在这里只使用ALM位来判断温度转换的结束与否,不难看出,当这个位置一时,标志着温度转换的完成,可以通过读取这个寄存器判断这个位来确定温度校准的状态,注意的是,由于在温度校准期间,不允许去对芯片做任何的SPI操作,否则会打断温度校准过程,导致校准失败,当温度校准完成以后,会把这个位设为1,标志校准完成,这样就可以操作DAC芯片了,从而输出更为精准的DAC电压值。
注意,由于温度校准期间不允许对DAC11001A芯片做任何的操作,所以需要延时等待2ms左右等待其校准结束,然后检测其标志位来看看校准状态,如果校准失败就可以在程序执行校准失败函数,就可以实现温度校准的检测部分。
image.png

硬件SPI驱动:

使用硬件SPI驱动DAC11001A,需要首先配置SPI的结构体

 SPI_InitTypeDef SPI_InitStruct;
    GPIO_InitTypeDef GPIO_InitStruct;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

    SPI_StructInit(&SPI_InitStruct); 
    SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStruct.SPI_DataWidth = 8;
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_Init(SPI1, &SPI_InitStruct);

    SPI_BiDirectionalLineConfig(SPI1, SPI_Enable_RX);
    SPI_BiDirectionalLineConfig(SPI1, SPI_Enable_TX);

配置SPI为SPI\_Mode\_Master,SPI\_DataSize\_8b,数据宽度为8,使用软件CS使能,把波特率分频器设置为32位,数据传输方式是高位在前传输。然后开启SPI的TX和RX功能。

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_5); /* PA4 SPI_NSS */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_5); /* PA5 SPI_SCK */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_5); /* PA6 SPI_MISO */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_5); /* PA7 SPI_MOSI */

    GPIO_StructInit(&GPIO_InitStruct);   
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_6;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    SPI_Cmd(SPI1, ENABLE);

配置GPIO的重映射方式和开启GPIO的时钟,配置GPIO然后开启。最后开启SPI,启动SPI转运功能。

void SPI_RxData_Polling(uint8_t *Buffer, uint8_t Length)
{
    uint8_t i = 0, Data = 0;

    for (i = 0; i < Length; i++)
    {
        SPI_SendData(SPI1, Data);

        while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_TXEPT))
        {
        }

        while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_RXAVL))
        {
        }

        Buffer[i] = SPI_ReceiveData(SPI1);
    }
}

这个函数是启动SPI阻塞转运的方式,由于没有使用DMA转运,所以在这里使用阻塞方式来转运SPI数据,每次接收结束查看标志位,查看完毕以后开启下一次转运。

void SPI_TxData_Polling(uint8_t *Buffer, uint8_t Length)
{
    uint8_t i = 0;
    volatile uint8_t Data = 0;

    for (i = 0; i < Length; i++)
    {
        SPI_SendData(SPI1, Buffer[i]);

        while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_TXEPT))
        {
        }

        while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_RXAVL))
        {
        }

        Data = SPI_ReceiveData(SPI1);
    }
}

这个函数是开启SPI阻塞发送,实现方法和接收函数一样。

void SPI_ReadBuffer(uint8_t *Buffer, uint8_t Length)
{
    if (Length)
    {
        SPI_FLASH_CS_L();
        SPI_RxData_Polling(Buffer, Length);
        SPI_FLASH_CS_H();
    }
}
void SPI_WriteBuffer(uint8_t *Buffer, uint8_t Length)
{
    if (Length)
    {
                GPIO_WriteBit(GPIOA,GPIO_Pin_3,Bit_SET);
        SPI_FLASH_CS_L();
        SPI_TxData_Polling(Buffer, Length);
        SPI_FLASH_CS_H();
                GPIO_WriteBit(GPIOA,GPIO_Pin_3,Bit_RESET);
    }
}

这个两个函数实现了封装的功能,封装的数据存入一个数组内,在此打开CS使能DAC11001A芯片,然后收发数据,是实现数组数据转运的功能。
接下来是逻辑的核心部分,实现了正弦波的产生和SPI发送数据:

uint8_t sine_wave_data[3];
int main(void)
{
        int32_t SIN =0;
        uint8_t TX1_Data[4] ={0x80,0x00,0x00,0x00};
        uint8_t TX2_Data[4] ={0x81,0x00,0x00,0x00};
        uint8_t TX3_Data[4] ={0x82,0x00,0x00,0x00};
        uint8_t TX4_Data[4] ={0x83,0x00,0x00,0x00};
        uint8_t TX5_Data[4] ={0x84,0x00,0x00,0x00};
        uint8_t TX6_Data[4] ={0x85,0x00,0x00,0x00};
        uint8_t TX7_Data[4] ={0x86,0x00,0x00,0x00};
        uint8_t Data1[4] ={0x01,0x00,0x00,0x00};
        uint8_t Data2[4] ={0x01,0x0F,0xFF,0xF0};
        uint8_t Data3[4] ={0x01,0x7F,0xFF,0xF0};
        uint8_t Data4[4] ={0x01,0xCF,0xFF,0xF0};
        uint8_t Data5[4] ={0x01,0xFF,0xFF,0xF0};
        uint8_t Init[4] ={0x02,0x00,0x0C,0x80};
        uint8_t RX_Data[7][4] ={0};
        PLATFORM_Init();
        LED_Config();
        SPI_Configure();
        SPI_WriteBuffer(Init,4);
      printf("zxcrr\r\n");
        SPI_WriteBuffer(TX1_Data,4);
        SPI_ReadBuffer(RX_Data[0],4);
        SPI_WriteBuffer(TX2_Data,4);
        SPI_ReadBuffer(RX_Data[1],4);
        SPI_WriteBuffer(TX3_Data,4);
        SPI_ReadBuffer(RX_Data[2],4);
        SPI_WriteBuffer(TX4_Data,4);
        SPI_ReadBuffer(RX_Data[3],4);
        SPI_WriteBuffer(TX5_Data,4);
        SPI_ReadBuffer(RX_Data[4],4);
        SPI_WriteBuffer(TX6_Data,4);
        SPI_ReadBuffer(RX_Data[5],4);
        SPI_WriteBuffer(TX7_Data,4);
        SPI_ReadBuffer(RX_Data[6],4);
        printf("zxcrr\r\n");
        for(uint8_t i=0;i<7;i++)
        {
                printf("0x%02x%02x%02x%02x\r\n",RX_Data[i][0],RX_Data[i][1],RX_Data[i][2],RX_Data[i][3]);
    }
        for(;;)
    {
             for (int i = 0; i < SAMPLES; i++) 
             {
                    double angle = 2.0 * 3.1415926 * i / SAMPLES;
                    int dac_value = (int)((sin(angle)) * (0x400000) )+0x6f0000;
                    Data1[1] = (dac_value >> 16) & 0xFF;
                    Data1[2] = (dac_value >> 8)  & 0xFF;
                    Data1[3] = (dac_value >> 4)  & 0xFF; // 去掉低4位对齐
                    SPI_WriteBuffer(Data1,4);
                
       }
    }
}


读取到的寄存器的数据如下,读取的数据和预期符合的很好:

这就是发出的正弦波的信号波形如下:


这就是整体图,可见产生的正弦波的信号符合预期。

DAC11001芯片输出的电压还是很稳定的,经过二阶滤波器输出的电压以及很平稳了,DAC11001A的应用场景还是很丰富的,包括电池充电测试,产生高精度输出电压等场合都很实用,所以这款芯片的价值还是很高的。
这就是Mini-F3575-OB开发板搭配DAC11001输出的一个简明教程,输出很稳定,这里是通过二阶滤波器滤波以后输出的DAC数值。
本文对DAC里的SPI通信方式进行一个简要的描述,并且演示了一如何使用SPI输出DAC数据,电压和接法,可以方便大伙进行学习和搭建电路。
DAC输出的稳定性和毛刺比单片机自己产生的低很多,还是专业的芯片做的强大(点赞)。
/*作者:是小枼大人🍡*/
/* --ZXYYL敬上--*/
/* --Clannad -- */

推荐阅读
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息