第37章_瑞萨MCU零基础入门系列教程之DAC数模转换模块

2023-09-14 22:26:58

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写,需要的同学可以在这里获取: https://item.taobao.com/item.htm?id=728461040949

配套资料获取:https://renesas-docs.100ask.net

瑞萨MCU零基础入门系列教程汇总https://blog.csdn.net/qq_35181236/article/details/132779862


第37章 DAC数模转换模块

37.1 SPI-DAC模块工作原理

本次实验使用的SPI-DAC模块是定制模块,原理图如下图所示:

核心芯片是TLC5615,主机通过SPI接口发出一个数字量,TCL5615将数字量转化为模拟量,并通过OUT引脚输出模拟电压来点亮LED。通过LED的亮度形象地感受DAC的效果。这个模块的参考电压是2.048V,可以输出的最大电压是2倍参考电压,即4.096V。

TLC5615是一个10bit的DAC转换芯片,用户需要将需要转换的数字量左移2bit后再通过SPI发送给TLC5165,数据格式和转换关系如下图所示:

由于TLC5615是10位DAC,它允许主控每次发送12位或者16位的数据,12位和16位的发送数据格式要求如下图所示。

这个模块的使用比较简单,重点是在SPI的通信上,其次是在发送数据的时候需要移位。

37.2 模块配置

本次实验使用的是开发板配套扩展板的SPI组,如下图所示:

使用的SPI引脚是P202/P203/P204和P205,SPI引脚对应使用的是RA6M5的Common SPI0:

本次实验使用的SPI-DAC模块控制比较简单,对于SPI的Stack配置使用默认参数即可,使能发送buffer空中断,配置中断对调函数,如下图所示:

37.3 外设驱动

37.3.1 GPIO驱动

本次实验的SPI片选信号脚为P205,它的驱动如下:

static struct IODev gSPIDACCSDev = {
    .name = "SPIDAC CS",
    .port = BSP_IO_PORT_02_PIN_05,
    .Init = IODrvInit,
    .Read = IODrvRead,
    .Write = IODrvWrite,
    .next = NULL
};

void IODevicesCreate(void)
{
    IODeviceInsert(&gSPIDACCSDev);
}

对于GPIO的驱动函数参考《32.4.1 GPIO驱动》。

37.3.2 SPI驱动

参考《35.4.2 SPI驱动》。

37.4 DAC驱动程序

37.4.1 SPI-DAC设备对象封装

要操纵SPI-DAC模块,只需要初始化、写入数值。为了更具观赏性,还可以提供写入多个数值的操作。把这些特性封装为一个结构体,代码如下(dev_spi_dac.h):

typedef struct SPIDACDev{
    char *name;
    int (*Init)(struct SPIDACDev *ptdev);
    int (*SetValue)(struct SPIDACDev *ptdev, float voltage);
    int (*Write)(struct SPIDACDev *ptdev, unsigned char *buf, unsigned int length);
}SPIDACDevice;

然后在dev_spi_dac.c里构造一个SPIDACDevice结构体,并给上层代码提高获得这个结构体的函数,代码如下:

static SPIDACDevice gDAC = {
    .name = "SPI DAC",
    .Init       = SPIDACDevInit,
    .SetValue   = SPIDACDevSetValue,
    .Write      = SPIDACDevWrite,
};

struct SPIDACDev *SPIDACGetDevice(void)
{
    return &gDAC;
}

37.4.2 初始化SPI-DAC

初始化SPI-DAC模块,本质就是初始化SPI控制器,代码如下:

static int SPIDACDevInit (struct SPIDACDev *ptdev)
{
    if(NULL == ptdev)   return -EINVAL;
    gSPI = SPIDeviceFind("SPIDAC SPI");
    if(NULL == gSPI)    return -ENODEV;
    if(ESUCCESS != gSPI->Init(gSPI))    return -EIO;
    return ESUCCESS;
}

37.4.3 输出一个模拟量

要输出指定数字量,需要根据TLC5615的数据格式进行移位计算,再通过SPI发送给TLC5615:

static int SPIDACDevSetValue (struct SPIDACDev *ptdev, float voltage)
{
    if(NULL == ptdev)   return -EINVAL;
    if(NULL == gSPI)    return -EINVAL;
    if(DAC_OUT_MAX_VOLTAGE < voltage)     return -EINVAL;

    unsigned short value = (unsigned short)((voltage*1024)/(DAC_OUT_MAX_VOLTAGE));
    value = (unsigned short)(value<<2);
    return gSPI->Write(gSPI, (unsigned char*)&value, 2);
}

37.4.4 输出N个模拟量

为了方便用户使用,将N个数字量在模块驱动函数内部进行格式转换,然后再通过SPI传输给转换芯片:

static int SPIDACDevWrite(struct SPIDACDev *ptdev, unsigned char *buf, unsigned int length)
{
    if(NULL == ptdev)   return -EINVAL;
    if(NULL == gSPI)    return -EINVAL;
    if(NULL == buf)     return -EINVAL;
    if(0 == length)     return -EINVAL;
    
    unsigned short *pbuf = (unsigned short*)buf;
    for(unsigned int i=0; i<length; i+=2)
    {
        pbuf[i] = (unsigned short)(pbuf[i]<<2);
    }
    
    return gSPI->Write(gSPI, buf, length);
}

37.5 测试程序

本次实验使用SPI传输,连续发送0~4V的电压给DAC模块,以实现呼吸灯效果:

void DeviceTest(void)
{
    UartDevicesRegister();
    TimerDevicesRegister();
    SPIDevicesRegister();
    IODevicesRegister();
    
    SPIDACDevice *pDevice = SPIDACGetDevice();
    if(NULL == pDevice)
    {
        xprintf("Failed to Find SPI DAC Devide!\r\n");
        return;
    }
    pDevice->Init(pDevice);
    
    bool dir = false;
    volatile float value = 0;
    while(1)
    {
        if(value > 4)
            dir = true;
        else if(value < 0)
            dir = false;
        
        if(dir)
            value += (float)0.5;
        else
            value -= (float)0.5;
        pDevice->SetValue(pDevice, value);
        mdelay(300);
    }
}

37.6 测试结果

将SPI-DAC模块插入到扩展板上后,再将程序烧写到开发板上运行,用户可以看到SPI-DAC模块上的LED呈现呼吸灯效果。


本章完
更多推荐

Linux线程之信号量(semaphore)

Linux信号量1.头文件2.类型2.1.类型值3.接口3.1.信号量接口3.1.1动态初始化资源3.1.2.动态释放资源3.1.3.信号量锁定3.1.4.带超时时间的信号量锁定3.1.5.不阻塞信号量锁定3.1.6.信号量解锁3.1.6.获取当前信号量的值4.示例4.1.信号量示例1.头文件#include<sema

Linux Day15:线程安全

一、线程安全方法线程安全即就是在多线程运行的时候,不论线程的调度顺序怎样,最终的结果都是一样的、正确的。那么就说这些线程是安全的。要保证线程安全需要做到:1)对线程同步,保证同一时刻只有一个线程访问临界资源。(信号量,互斥锁,读写锁,条件变量)2)在多线程中使用线程安全的函数(可重入函数),所谓线程安全的函数指的是:如

Linux动态库

定义:动态函数库,是在程序执行时动态(临时)由目标程序去调用优点:调用时不复制,程序运行时动态加载到内存,供程序调用,系统只加载一次,多个程序可以共用,节省内存。程序升级简单,因为app里面没有库的源代码,升级之后只要库的名字不变,函数名以及参数不变,只是实现做了优化,就能加载成功。缺点:加载速度比静态库慢,占用内存大

《动手学深度学习》(pytorch版+mxnet版)2023最新

我又来推书了,这次分享的这本书可是重量级,目前已经被55个国家300所大学用于教学,同时受到了学术界与工业界的强烈推荐。这本书就是李沐、阿斯顿·张、立顿、斯莫拉四位大佬联合编写的《动手学深度学习》。本书面向中文读者,能运行、可讨论,适合本科生、研究生、工程师以及研究人员学习。书籍pdf文末获取书籍介绍全书采用公式+图示

安卓设备监听全部输入信号

前言:最近团队收到一个产品需求,需要监听安卓设备上用户是否有输入行为,以免定制推荐的时候打搅到用户。这里指的是设备上所有应用的输入行为,而不是单指某一个应用。这个需求还是蛮有挑战性的,需要涉及到很多FW层的知识,所以围绕着这个需求,定制了多个方案,并且也找了许多人进行讨论,总算有了一个相对可行的方案,因此,通过本文记录

图像处理的创意之旅:逐步攀登Python OpenCV的高峰

目录介绍OpenCV简介安装OpenCV加载和显示图像图像处理目标检测图像处理的高级应用视频处理综合案例:人脸识别应用总结介绍欢迎来到本篇文章,我们将一起探索如何使用Python中的OpenCV库进行图像处理和计算机视觉任务。无论您是初学者还是有一定编程经验的开发者,本文将从入门到精通地引导您,帮助您理解OpenCV的

Windows 上的本机 Android 开发入门

🎬岸边的风:个人主页🔥个人专栏:《VUE》《javaScript》⛺️生活的理想,就是为了理想的生活!目录安装AndroidStudio创建新项目Java或Kotlin最低API级别即时应用支持和Androidx项目项目文件使用C或C++进行Android游戏开发设计指南FluentDesignSystemforA

在 Android 设备或仿真器上进行测试

🎬岸边的风:个人主页🔥个人专栏:《VUE》《javaScript》⛺️生活的理想,就是为了理想的生活!目录WindowsDefender概述如何将排除项添加到WindowsDefenderAndroid开发时要考虑的排除项本指南介绍如何在WindowsDefender安全设置中设置排除项,以便在使用Windows计

vue基础知识十二:双向数据绑定是什么

一、什么是双向绑定我们先从单向绑定切入单向绑定非常简单,就是把Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新双向绑定就很容易联想到了,在单向绑定的基础上,用户更新了View,Model的数据也自动被更新了,这种情况就是双向绑定举个栗子当用户填写表单时,View的状态就被

第 113 场 LeetCode 双周赛题解

A使数组成为递增数组的最少右移次数数据范围小直接模拟…classSolution{public:intminimumRightShifts(vector<int>&nums){for(intop=0;op<nums.size();op++){if(is_sorted(nums.begin(),nums.end()))/

udp的简单整理

最近思考udp处理的一些细节,根据公开课,反复思考,终于有所理解,做整理备用。0:简单汇总1:udp是基于报文传输的,接收方收取数据时要一次性读完。2:借助udp进行发包,发大包也是没有问题的,借助IP层ip分片。===》ip分片可以发生在原始主机上,也可以发生在中间路由器上(MTU值)===》ip分片后,可以再分片,

热文推荐