ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

18-CubeMx+Keil+Proteus仿真STM32 - DAC

2022-05-31 12:03:58  阅读:308  来源: 互联网

标签:vSPI DAC 18 Keil 正弦波 SPI GPIO MCP4921


本文例子参考《STM32单片机开发实例——基于Proteus虚拟仿真与HAL/LL库》
源代码:https://github.com/LanLinnet/STM33F103R6

项目要求

在SPI总线通信的基础上,使用单片机控制DAC芯片MCP4921以1秒为周期输出正弦波,正弦波的波动范围为0-3.3V。

硬件设计

  1. 第一节的基础上,在Proteus中添加电路如下图所示。其中我们添加了一个DAC芯片MCP4921

    此外,我们还添加了两个虚拟仪表:一个示波器OSCILLOSCOPE和一个SPI总线调试工具SPI DEBUGGER

  2. MCP4921:
    1)简介:STM32F103R6单片机本身不自带DAC,如果设计到数模转换的项目,可以选择DAC芯片MCP4921。MCP4921是美国Microchip公司的串行12位DAC芯片,兼容SPI,最高通信频率为20MHz,一次转换时间为4.5μs,工作电压为2.7-5.5V。
    2)引脚:MCP4921引脚的功能如下表所示。

    3)通信数据格式:MCP4921只有数据输入,没有数据输出,单片机只需要将16位数据(12位数字量和4位配置信息)一起打包发给DAC芯片,DAC随即开始数模转换过程。MCP4921通信数据格式如下表所示。

    • \(\overline A/B\)位:对于MCP4921,由于只有A通道,所以该位只能选0。
    • BUF位:参考电压\(V_{REF}\)输入缓冲器控制位,设1时缓冲,设0时未缓冲。
    • \(\overline{GA}\)位:输出增益选择位,设1时无增益,设0时两倍增益。
    • \(\overline{SHDN}\)位:待机模式设置为,设1时不进入待机模式,设0时进入待机模式。
  3. 正弦波形的生成:
    1)存在问题:MCP4921是12位DAC芯片,因此输入数字量的范围是0x000-0x3FF,输出模拟量电压范围为0-\(V_{REF}\),即无法输出负电压,那么就无法输出完整的正弦波形。
    2)解决方案:

    • 通过外围元器件搭建调理电路使电路能够输出负电压。
    • 将正弦波信号沿纵轴(电压/数字量)正向移动,确保波谷也位于横轴(时间)的上方。

    3)采样表:这里我们选择后一个方案,可以推出正弦波计算公式为
    \(D=512\times\sin\left(2\pi\;t\right)+512\)
    为了提高单片机CPU的执行效率,这里我们使用查表法。在1秒内,每隔0.02秒计算一次采样值,其采样表如下表所示。

  4. 打开CubeMX,建立工程。STM32F103R6单片机自带一个SPI模块,但是为了便于移植,本项目中采用GPIO引脚模拟SPI时序。设置PA4、PA5、PA7均为GPIO_Output点击“Categories”中的“GPIO”,修改GPIO各参数如下图所示。有关SPI通信部分可以参考第17节

  5. 点击“Generator Code”生成Keil工程。

软件编写

  1. 考虑到代码的可移植性,这里将SPI和MCP4921的驱动代码全部封装成函数并分别归入头文件“vSPI.h”和“MCP4921.h”中。我们可以先在...\Core\Src文件夹中建立这两个头文件,此时Keil可能找不到对应文件,可以直接将文件拽入Keil中进行编辑,然后再在“main.c”文件中进行include。

  2. 点击“Open Project”在Keil中打开工程,打开“vSPI.h”,添加代码如下。

    #ifndef INC_VSPI_H_
    #define INC_VSPI_H_
    
    #include "main.h"
    
    //软件延时函数,单位为微秒
    void delay_us(uint16_t n)
    {
      uint16_t i = n * 8;
      while(i--);
    }
    
    //SPI总线使能
    void vSPI_En()
    {
      HAL_GPIO_WritePin(GPIOA, vnCS_Pin, GPIO_PIN_RESET);
      HAL_GPIO_WritePin(GPIOA, vSCK_Pin, GPIO_PIN_RESET);
      delay_us(4);
    }
    
    //SPI总线禁止
    void vSPI_Dis()
    {
      HAL_GPIO_WritePin(GPIOA, vSCK_Pin, GPIO_PIN_SET);
      HAL_GPIO_WritePin(GPIOA, vnCS_Pin, GPIO_PIN_SET);
    }
    
    //SPI主站发送1字节
    void vSPI_SndByte(uint8_t dat)		//dat表示发送的字节
    {
      uint8_t i;
      for(i=0; i<8; i++)
      {
        HAL_GPIO_WritePin(GPIOA, vSCK_Pin, GPIO_PIN_RESET);
        delay_us(4);
        if(dat & 0x80)
        {
          HAL_GPIO_WritePin(GPIOA, vMOSI_Pin, GPIO_PIN_SET);
        }
        else 
          HAL_GPIO_WritePin(GPIOA, vMOSI_Pin, GPIO_PIN_RESET);
        dat<<=1;
        //上升沿
        HAL_GPIO_WritePin(GPIOA, vSCK_Pin, GPIO_PIN_SET);
        delay_us(4);
      }
    }
    
    #endif /* INC_VSPI_H_ */
    

    打开“MCP4921.h”,添加代码如下。

    #ifndef INC_MCP4921_H_
    #define INC_MCP4921_H_
    
    #include "main.h"
    #include "vSPI.h"
    
    //写入MCP4921: Cmd-指令(仅高4位)  Dat-数据(12位)
    void MCP4921Write(uint8_t Cmd, uint16_t Dat)
    {
      uint8_t DatM, DatL;		//数据高字节、低字节
      DatL = (uint8_t)(Dat & 0x00ff);	
      DatM = (uint8_t)((Dat>>8) & 0x00ff);
      vSPI_En();		//SPI总线使能
      vSPI_SndByte(0x70|DatM);		//先写高字节
      vSPI_SndByte(DatL);		//再写低字节
      vSPI_Dis();		//SPI总线禁止
    }
    #endif /* INC_MCP4921_H_ */
    
  3. 随后我们需要在main.c文件中的最前面引入我们自定义的头文件

    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    #include "vSPI.h"		//引入自定义头文件
    #include "MCP4921.h"
    /* USER CODE END Includes */
    

    在全局中定义正弦波输出的表

    /* USER CODE BEGIN PV */
    //查表法
    static uint16_t tD[50] = 
    {
    	512, 576, 639, 700, 759, 813, 862, 907, 944, 975,
    	999, 1015, 1023, 1023, 1015, 999, 975, 944, 907, 862,
    	813, 759, 700, 639, 576, 512, 448, 385, 324, 265, 
    	211, 162, 117, 80, 49, 25, 9, 1, 1, 9,
    	25, 49, 80, 117, 162, 211, 265, 324, 385, 448
    };
    /* USER CODE END PV */
    

    最后,在main函数中定义循环变量,并调用我们自定义的函数每隔20ms计算一次采样值并输出

    /* USER CODE BEGIN 1 */
    int i;		//循环变量i
    /* USER CODE END 1 */
    
    /* USER CODE BEGIN WHILE */
    while (1)
    {
      for(i=0; i<50; i++)
      {
        MCP4921Write(0x70, tD[i]);
        HAL_Delay(20);		//每隔20ns计算(输出)1次采样值
      }
    /* USER CODE END WHILE */
    
    /* USER CODE BEGIN 3 */
    }
    /* USER CODE END 3 */
    

联合调试

  1. 点击运行,生成HEX文件。
  2. 在Proteus中加载相应HEX文件,点击运行。可以看到示波器中显示的波形为正弦波(注意示波器的调整)。

标签:vSPI,DAC,18,Keil,正弦波,SPI,GPIO,MCP4921
来源: https://www.cnblogs.com/sheepeach/p/STM32F103_DAC.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有