【RTT-Studio】详细使用教程七:SGM5352外部DAC使用

news2024/11/14 13:53:11

文章目录

    • 一、简介
    • 二、RTT时钟配置
    • 三、初始化配置
    • 四、完整代码
    • 五、测试验证

一、简介

本文主要介绍使用RTT-ThreadStudio来驱动SGM5352芯片的使用,该芯片主要是一个低功率,4通道,16位,电压输出DAC。它从2.7V到5.5V,设计保证了单调性。SGM5352-16通过使用外部参考电压来设置每个DAC通道的输出范围。它包含了一个电源复位电路,确保DAC输出功率到0V。SGM5352-16使用了一个3线串行SPI接口。
相关资料:
(1)芯片资料:SGM5352芯片手册
(2)参考电路:SGM5352参考电路设计

芯片使用:

  • SGM5352-16是一个16位电阻串DAC,理想输出电压可根据以下公式计算:Voutx = (Vrefh - Vrefl) * Din / 65535
  • 三线制串行接口(nSYNC、SCLK和DIN/OUT)兼容SPI接口标准。
  • 在正常操作中,启用销必须设置为较低。当启用销变高时,SGM5352-16对任何操作都没有响应。
    在这里插入图片描述

寄存器操作:
1.读取数据

二进制备注
0001 10000x18 —读取通道A数据
0001 10100x1A —读取通道B数据
0001 11000x1C —读取通道C数据
0001 11100x1D —读取通道D数据

2.指定操作

二进制备注
0001 00000x10 0x1234 —写通道A
0001 00100x12 0x3456 —写通道B
0001 01000x14 0x4567 —写通道C
0001 01100x16 0x6789 —写通道D
0001 00010x11 0x1234 —写通道A关闭
0001 00110x13 0x3456 —写通道B关闭
0001 01010x15 0x4567 —写通道C关闭
0001 01110x17 0x6789 —写通道D关闭

3.广播模式

二进制备注
0011 X0X0 X0x30 0x0000 同时使用每个通道临时寄存器中存储的数据更新系统中所有设备的所有通道。
0011 X1X0 data0x34 0x1234 写入所有设备并加载所有数据的dac。
0011 X1X1 0x00000x35 0x0000 写入所有设备,并在DB中使用关机命令加载所有DAC。详见表1。
0011 X0X1 0x80000x31 0x8000 启用SPI输出函数。
0011 X0X1 0x00000x31 0x0000 禁用SPI输出函数。

读写流程
1.先对CS、CLK和MOSI引脚进行初始化配置,以及初始电平设置。
2.写MOSI的模式切换函数,在接收数据的时候,需要进行MOSI引脚模式的切换。
3.编写寄存器读写函数
4.读写数据。写数据可以直接使用写寄存器函数即可,读数据时需要注意先使能SPI函数,再使用读寄存器函数。

PS:数据接收测试会有问题,如果哪位大佬有见解,可以分享一下!!!


二、RTT时钟配置

由于使用RTT生成的工程默认使用的是系统内部时钟,便于我们对时间的控制,所以通常会使用外部时钟,因此需要对工程中的时钟进行更改,更改内容如下:

  • 打开RT-Thread Studio软件新建基于芯片的项目,并使用外部时钟系统。
  • 在drv_clk.c文件中添加时钟配置函数,并且注释内部时钟的调用。
/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    /** Configure the main internal regulator output voltage
     */
    __HAL_RCC_PWR_CLK_ENABLE();
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

    /** Initializes the CPU, AHB and APB busses clocks
     */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 4;
    RCC_OscInitStruct.PLL.PLLN = 168;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
    RCC_OscInitStruct.PLL.PLLQ = 4;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        Error_Handler();
    }

    /** Initializes the CPU, AHB and APB busses clocks
     */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
            | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
    {
        Error_Handler();
    }
}

void clk_init(char *clk_source, int source_freq, int target_freq)
{
//    system_clock_config(target_freq);
    SystemClock_Config();
}

三、初始化配置

本文主要是使用SGM5352芯片输出三角波,以及控制其他通道数据不同的电平。
1.定时器初始化
先在board.h文件中将定时器打开,便于使用定时器进行数据的发送,如下:

/** if you want to use hardware timer you can use the following instructions.
 *
 * STEP 1, open hwtimer driver framework support in the RT-Thread Settings file
 *
 * STEP 2, define macro related to the hwtimer
 *                 such as     #define BSP_USING_TIM  and
 *                             #define BSP_USING_TIM1
 *
 * STEP 3, copy your hardwire timer init function from stm32xxxx_hal_msp.c generated by stm32cubemx to the end of board.c file
 *                 such as     void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
 *
 * STEP 4, modify your stm32xxxx_hal_config.h file to support hardwere timer peripherals. define macro related to the peripherals
 *                 such as     #define HAL_TIM_MODULE_ENABLED
 *
 */

#define BSP_USING_TIM
#ifdef BSP_USING_TIM
#define BSP_USING_TIM3
#endif

在board.c中添加定时器的时钟初始化函数,基本函数如下:

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
    if (tim_baseHandle->Instance == TIM3)
    {
        /* Peripheral clock enable */
        __HAL_RCC_TIM3_CLK_ENABLE();
    }
}

在图形化界面中将定时器的驱动打开,如下:
在这里插入图片描述

如果定时器初始化函数没有定义,需要在drivers->include->config->tim_config.h中调价以下内容:

#ifdef BSP_USING_TIM3
#ifndef TIM3_CONFIG
#define TIM3_CONFIG                                         \
    {                                                       \
       .tim_handle.Instance     = TIM3,                     \
       .tim_irqn                = TIM3_IRQn,                \
       .name                    = "timer3",                 \
    }
#endif /* TIM3_CONFIG */
#endif /* BSP_USING_TIM3 */

2.SGM5352驱动代码
SGM5352写数据函数,总共需要发送数据24个字节的数据,可以分成两次发送:

/**
 * @brief SGM5352写数据
 * @param config  :写地址
 * @param DAC_DATA:写入的数据
 */
void SGM5352_WHITE(uint8_t config, uint16_t DAC_DATA)
{
    SPI_GPIO_Mode_Change(GPIO_MODE_OUTPUT_PP);

    CLK_OUTPUT_HIGH;
    CS_OUTPUT_HIGH;

    CS_OUTPUT_LOW;

    for (uint8_t i = 0; i < 8; ++i)
    {
        if (config & (1 << (7 - i)))
        {
            MOSI_OUTPUT_HIGH;
        }
        else
        {
            MOSI_OUTPUT_LOW;
        }

        CLK_OUTPUT_LOW;
        CLK_OUTPUT_HIGH;
    }

    for (int i = 0; i < 16; ++i)
    {
        if (DAC_DATA & (1 << (15 - i)))
        {
            MOSI_OUTPUT_HIGH
        }
        else
        {
            MOSI_OUTPUT_LOW;
        }

        CLK_OUTPUT_LOW;
        CLK_OUTPUT_HIGH;
    }

    CS_OUTPUT_HIGH;
}

SGM5352读数据函数,在使用的时候需要提前打开SPI输出函数使能,并且该函数在发送完前8个字节数据后,需要更改模式为输入模式,从未接收芯片返回的数据。

/**
 * @brief SGM5352读数据
 * @param config:读取的地址
 * @return 返回读取到的数据
 */
uint16_t SGM5352_READ(uint8_t config)
{
    uint16_t data = 0;

    SPI_GPIO_Mode_Change(GPIO_MODE_OUTPUT_PP);

    CLK_OUTPUT_HIGH;
    CS_OUTPUT_HIGH;

    CS_OUTPUT_LOW;

    for (int i = 0; i < 8; ++i)
    {
        if (config & (1 << (7 - i)))
        {
            MOSI_OUTPUT_HIGH;
        }
        else
        {
            MOSI_OUTPUT_LOW;
        }

        CLK_OUTPUT_LOW;

        CLK_OUTPUT_HIGH;
    }

    SPI_GPIO_Mode_Change(GPIO_MODE_INPUT);

    for (int i = 0; i < 16; ++i)
    {
        CLK_OUTPUT_LOW;
        data = (data << 1) | MOSI_INPUT;
        CLK_OUTPUT_HIGH;
    }

    CS_OUTPUT_HIGH;

    return data;
}

3.定时发送数据
输出三角波时,需要通过定时器进行数据的实时更新,并且需要更新三角波的缓冲区。时钟计算:f = 1 / ((40*100)/1000000) ==> t = 1000000 / (100 * f):其中1000000是计数频率,f是频率,100是采样点的个数。

/**
 * @brief 三角波信号缓存填充
 */
void Signal_Ramp(float value)
{
    uint16_t dac = value * 65535 / 2.5;

    for (int i = 0; i < 50; i++)
    {
        signalRampBuffer[i] = i * dac / 50;
    }

    for (int i = 50; i < 100; i++)
    {
        signalRampBuffer[i] = (100 - i) * dac / (100 - 50);
    }
}

/**
 * @brief 定时器超时回调函数
 * @param dev
 * @param size
 * @return
 */
static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
{
    rt_sem_release(dac_sem);

    return 0;
}

/**
 * @brief 启动定时器,设置定时时间
 * @param fre:设置频率
 */
int start_Hwtimer(uint16_t fre)
{
    rt_err_t ret = RT_EOK;
    rt_hwtimer_mode_t mode;     /* 定时器模式 */
    rt_uint32_t freq = 1000000; /* 计数频率 */

    if (dev_state == 0)
    {
        dev_state = 1;

        /* 查找定时器设备 */
        hw_dev = rt_device_find(HWTIMER_DEV_NAME);
        RT_ASSERT(hw_dev != RT_NULL);

        /* 以读写方式打开设备 */
        ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
        RT_ASSERT(ret == RT_EOK);

        /* 设置超时回调函数 */
        rt_device_set_rx_indicate(hw_dev, timeout_cb);

        /* 设置计数频率(若未设置该项,默认为1Mhz 或 支持的最小计数频率) */
        rt_device_control(hw_dev, HWTIMER_CTRL_FREQ_SET, &freq);

        /* 设置模式为周期性定时器(若未设置,默认是HWTIMER_MODE_ONESHOT)*/
        mode = HWTIMER_MODE_PERIOD;
        ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
        RT_ASSERT(ret == RT_EOK);
    }

    /* 设置定时器超时值,并启动定时器 */
    rt_hwtimerval_t timeout_s;
    // f = 1 / ((40*100)/1000000) ==> t = 1000000 / (100 * f)
    timeout_s.sec = 0;
    timeout_s.usec = 1000000 / (100 * fre);
    if (rt_device_write(hw_dev, 0, &timeout_s, sizeof(timeout_s)) != sizeof(timeout_s))
    {
        rt_kprintf("set timeout value failed\n");
        return RT_ERROR;
    }

    return 0;
}

/**
 * @brief 关闭定时器
 */
void close_Hwtimer(void)
{
    dev_state = 0;
    rt_device_close(hw_dev);
    SGM5352_WHITE(ADDR_CHANNELA_CLOSE, 0);
}

四、完整代码

1.sgm5352.c :可以使用控制台来控制每个通道的输出DAC值,并且可以更改三角波的频率和输出电压:

#include "sgm5352.h"

/**
 * @brief SEG5352的GPIO引脚初始化
 */
void SEG5352_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOF_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();

    /*Configure GPIO pin : CS_Pin */
    GPIO_InitStruct.Pin = CS_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(CS_GPIO_Port, &GPIO_InitStruct);

    /*Configure GPIO pins : CLK_Pin MOSI_Pin */
    GPIO_InitStruct.Pin = CLK_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(CLK_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = MOSI_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(MOSI_GPIO_Port, &GPIO_InitStruct);

    /*Configure GPIO pin Output Level */
    HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);

    /*Configure GPIO pin Output Level */
    HAL_GPIO_WritePin(CLK_GPIO_Port,  CLK_Pin,  GPIO_PIN_RESET);
    HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, GPIO_PIN_RESET);

    dac_sem = rt_sem_create("dac_sem", 0, RT_IPC_FLAG_PRIO);
}

/**
 * @brief SPI引脚模式切换
 * @param Mode:GPIO_MODE_OUTPUT_PP、GPIO_MODE_INPUT
 */
void SPI_GPIO_Mode_Change(uint32_t Mode)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_GPIOB_CLK_ENABLE();

    /*Configure GPIO pins : CLK_Pin MOSI_Pin */
    GPIO_InitStruct.Pin = MOSI_Pin;
    GPIO_InitStruct.Mode = Mode;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

/**
 * @brief SGM5352写数据
 * @param config  :写地址
 * @param DAC_DATA:写入的数据
 */
void SGM5352_WHITE(uint8_t config, uint16_t DAC_DATA)
{
    SPI_GPIO_Mode_Change(GPIO_MODE_OUTPUT_PP);

    CLK_OUTPUT_HIGH;
    CS_OUTPUT_HIGH;

    CS_OUTPUT_LOW;

    for (uint8_t i = 0; i < 8; ++i)
    {
        if (config & (1 << (7 - i)))
        {
            MOSI_OUTPUT_HIGH;
        }
        else
        {
            MOSI_OUTPUT_LOW;
        }

        CLK_OUTPUT_LOW;
        CLK_OUTPUT_HIGH;
    }

    for (int i = 0; i < 16; ++i)
    {
        if (DAC_DATA & (1 << (15 - i)))
        {
            MOSI_OUTPUT_HIGH
        }
        else
        {
            MOSI_OUTPUT_LOW;
        }

        CLK_OUTPUT_LOW;
        CLK_OUTPUT_HIGH;
    }

    CS_OUTPUT_HIGH;
}

/**
 * @brief SGM5352读数据
 * @param config:读取的地址
 * @return 返回读取到的数据
 */
uint16_t SGM5352_READ(uint8_t config)
{
    uint16_t data = 0;

    SPI_GPIO_Mode_Change(GPIO_MODE_OUTPUT_PP);

    CLK_OUTPUT_HIGH;
    CS_OUTPUT_HIGH;

    CS_OUTPUT_LOW;

    for (int i = 0; i < 8; ++i)
    {
        if (config & (1 << (7 - i)))
        {
            MOSI_OUTPUT_HIGH;
        }
        else
        {
            MOSI_OUTPUT_LOW;
        }

        CLK_OUTPUT_LOW;

        CLK_OUTPUT_HIGH;
    }

    SPI_GPIO_Mode_Change(GPIO_MODE_INPUT);

    for (int i = 0; i < 16; ++i)
    {
        CLK_OUTPUT_LOW;
        data = (data << 1) | MOSI_INPUT;
        CLK_OUTPUT_HIGH;
    }

    CS_OUTPUT_HIGH;

    return data;
}

/**
 * @brief 三角波信号缓存填充
 */
void Signal_Ramp(float value)
{
    uint16_t dac = value * 65535 / 2.5;

    for (int i = 0; i < 50; i++)
    {
        signalRampBuffer[i] = i * dac / 50;
    }

    for (int i = 50; i < 100; i++)
    {
        signalRampBuffer[i] = (100 - i) * dac / (100 - 50);
    }
}

/**
 * @brief 定时器超时回调函数
 * @param dev
 * @param size
 * @return
 */
static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
{
    rt_sem_release(dac_sem);

    return 0;
}

/**
 * @brief 启动定时器,设置定时时间
 * @param fre:设置频率
 */
int start_Hwtimer(uint16_t fre)
{
    rt_err_t ret = RT_EOK;
    rt_hwtimer_mode_t mode;     /* 定时器模式 */
    rt_uint32_t freq = 1000000; /* 计数频率 */

    if (dev_state == 0)
    {
        dev_state = 1;

        /* 查找定时器设备 */
        hw_dev = rt_device_find(HWTIMER_DEV_NAME);
        RT_ASSERT(hw_dev != RT_NULL);

        /* 以读写方式打开设备 */
        ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
        RT_ASSERT(ret == RT_EOK);

        /* 设置超时回调函数 */
        rt_device_set_rx_indicate(hw_dev, timeout_cb);

        /* 设置计数频率(若未设置该项,默认为1Mhz 或 支持的最小计数频率) */
        rt_device_control(hw_dev, HWTIMER_CTRL_FREQ_SET, &freq);

        /* 设置模式为周期性定时器(若未设置,默认是HWTIMER_MODE_ONESHOT)*/
        mode = HWTIMER_MODE_PERIOD;
        ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
        RT_ASSERT(ret == RT_EOK);
    }

    /* 设置定时器超时值,并启动定时器 */
    rt_hwtimerval_t timeout_s;
    // f = 1 / ((40*100)/1000000) ==> t = 1000000 / (100 * f)
    timeout_s.sec = 0;
    timeout_s.usec = 1000000 / (100 * fre);
    if (rt_device_write(hw_dev, 0, &timeout_s, sizeof(timeout_s)) != sizeof(timeout_s))
    {
        rt_kprintf("set timeout value failed\n");
        return RT_ERROR;
    }

    return 0;
}

/**
 * @brief 关闭定时器
 */
void close_Hwtimer(void)
{
    dev_state = 0;
    rt_device_close(hw_dev);
    SGM5352_WHITE(ADDR_CHANNELA_CLOSE, 0);
}

/**
 * @brief 设置三角波数据
 * @param argc
 * @param argv
 */
void Set_Signal_Ramp_Param(int argc, char **argv)
{
    RT_ASSERT(argc == 3 || argc == 2);
    if (strcmp(argv[0], "signal") == 0)
    {
        if (strcmp(argv[1], "vol") == 0)
        {
            Signal_Ramp(atof(argv[2]));
        }
        else if (strcmp(argv[1], "fre") == 0)
        {
            start_Hwtimer(atoi(argv[2]));
        }
        else if (strcmp(argv[1], "stop") == 0)
        {
            close_Hwtimer();
        }
    }
}
MSH_CMD_EXPORT_ALIAS(Set_Signal_Ramp_Param, signal, Set Signal Ramp Param);

/**
 * @brief 设置其他三个通道参数
 * @param argc
 * @param argv
 */
void Set_Channel_Param(int argc, char **argv)
{
    RT_ASSERT(argc == 3 || argc == 2);

    uint16_t dac = atof(argv[2]) * 65535 / 2.5;
    if (strcmp(argv[0], "set") == 0)
    {
        if (strcmp(argv[1], "B") == 0)
        {
            SGM5352_WHITE(ADDR_CHANNELB_W, dac);
        }
        else if (strcmp(argv[1], "C") == 0)
        {
            SGM5352_WHITE(ADDR_CHANNELC_W, dac);
        }
        else if (strcmp(argv[1], "D") == 0)
        {
            SGM5352_WHITE(ADDR_CHANNELD_W, dac);
        }
        else if (strcmp(argv[1], "stop_B") == 0)
        {
            SGM5352_WHITE(ADDR_CHANNELB_CLOSE, 0);
        }
        else if (strcmp(argv[1], "stop_C") == 0)
        {
            SGM5352_WHITE(ADDR_CHANNELC_CLOSE, 0);
        }
        else if (strcmp(argv[1], "stop_D") == 0)
        {
            SGM5352_WHITE(ADDR_CHANNELD_CLOSE, 0);
        }
    }
}
MSH_CMD_EXPORT_ALIAS(Set_Channel_Param, set, Set Channel Param);

2.sgm5352.h

#ifndef APPLICATIONS_SGM5352_H_
#define APPLICATIONS_SGM5352_H_

#include <drv_common.h>
#include <string.h>
#include <stdlib.h>

/**====================================================###### 宏定义 ######==================================================*/
#define BOARD_CAST_MODE     0x34            // 广播模式地址

#define ADDR_CHANNELA_W     0x10            // 写通道A地址
#define ADDR_CHANNELB_W     0x12            // 写通道B地址
#define ADDR_CHANNELC_W     0x14            // 写通道C地址
#define ADDR_CHANNELD_W     0x16            // 写通道D地址

#define ADDR_CHANNELA_R     0x18            // 读取通道A数据
#define ADDR_CHANNELB_R     0x1A            // 读取通道B数据
#define ADDR_CHANNELC_R     0x1C            // 读取通道C数据
#define ADDR_CHANNELD_R     0x1D            // 读取通道D数据

#define ADDR_CHANNELA_CLOSE 0x11            // 写通道A关闭
#define ADDR_CHANNELB_CLOSE 0x13            // 写通道A关闭
#define ADDR_CHANNELC_CLOSE 0x15            // 写通道A关闭
#define ADDR_CHANNELD_CLOSE 0x17            // 写通道A关闭

#define SPI_OUTPUT          0x31            // 启用SPI输出函数
#define SPI_OUTPUT_ENABLE   0x8000          // 启用SPI发送数据位
#define SPI_OUTPUT_UNENABLE 0x0000          // 禁用SPI发送数据位

#define CS_Pin              GPIO_PIN_6      // 片选引脚
#define CS_GPIO_Port        GPIOF
#define CLK_Pin             GPIO_PIN_10     // 时钟引脚
#define CLK_GPIO_Port       GPIOB
#define MOSI_Pin            GPIO_PIN_2      // 主输出从输入
#define MOSI_GPIO_Port      GPIOC

#define CS_OUTPUT_HIGH      HAL_GPIO_WritePin(CS_GPIO_Port,   CS_Pin,   GPIO_PIN_SET);
#define CS_OUTPUT_LOW       HAL_GPIO_WritePin(CS_GPIO_Port,   CS_Pin,   GPIO_PIN_RESET);
#define CLK_OUTPUT_HIGH     HAL_GPIO_WritePin(CLK_GPIO_Port,  CLK_Pin,  GPIO_PIN_SET);
#define CLK_OUTPUT_LOW      HAL_GPIO_WritePin(CLK_GPIO_Port,  CLK_Pin,  GPIO_PIN_RESET);
#define MOSI_OUTPUT_HIGH    HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, GPIO_PIN_SET);
#define MOSI_OUTPUT_LOW     HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, GPIO_PIN_RESET);

#define MOSI_INPUT          HAL_GPIO_ReadPin(MOSI_GPIO_Port, MOSI_Pin);

#define HWTIMER_DEV_NAME   "timer3"         // 定时器名称
TIM_HandleTypeDef htim3;

uint16_t signalRampBuffer[100];             // 三角波数据缓冲区
rt_device_t hw_dev;                         // 定时器设备句柄
rt_uint8_t dac_index;                       // DAC序号
rt_uint8_t dev_state;                       // 设备状态
rt_sem_t dac_sem;                           // dac发送信号量
/**====================================================#######  END  #######=================================================*/

/**=================================================##### 函数及变量声明 #####===============================================*/
extern void SEG5352_GPIO_Init(void);
extern void SGM5352_WHITE(uint8_t config, uint16_t DAC_DATA);
extern uint16_t SGM5352_READ(uint8_t config);
extern void Signal_Ramp(float value);
extern void Hwtimer_Init(void);
extern int start_Hwtimer(uint16_t fre);
/**====================================================#######  END  #######=================================================*/

#endif /* APPLICATIONS_SGM5352_H_ */

3.main.c

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#include "sgm5352.h"

int main(void)
{
    int count = 1;

    SEG5352_GPIO_Init();

    while (count)
    {
        rt_err_t result = rt_sem_take(dac_sem, RT_WAITING_FOREVER);
        if (result == RT_EOK)
        {
            SGM5352_WHITE(ADDR_CHANNELA_W, signalRampBuffer[dac_index++]);
            if (dac_index >= 100) dac_index = 0;
        }
    }

    return RT_EOK;
}

五、测试验证

1.通过控制台来进行数据的设置、控制输出:指令分别如下

指令功能
signal vol 2设置三角波电压2V
signal fre 40设置三角波频率40Hz
signal stop关闭三角波输出
set B 1设置通道B电压为1V
set C 2设置通道C电压为2V
set D 2.5设置通道D电压为2.5V
set stop_B关闭通道B输出
set stop_C关闭通道C输出
set stop_D关闭通道D输出

2.实验现象
通过控制台进行参数设置后,通道的输出情况:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1974495.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

短视频矩阵系统设计:抖音短视频平台的最佳选择

随着移动互联网的快速发展&#xff0c;短视频行业异军突起&#xff0c;抖音短视频平台凭借其丰富的内容、便捷的创作工具和智能推荐算法&#xff0c;吸引了大量用户。在这个背景下&#xff0c;短视频矩阵系统应运而生&#xff0c;成为抖音短视频平台的最佳选择。本文将详细介绍…

左手坐标系、右手坐标系、坐标轴方向

一、右手坐标系 1、y轴朝上&#xff1a;webgl、Threejs、Unity、Unreal、Maya、3D Builder x&#xff1a;向右y&#xff1a;向上z&#xff1a;向前&#xff08;朝向观察者、指向屏幕外&#xff09; 2、z轴朝上&#xff1a;cesium、blender x&#xff1a;向右y&#xff1a;向前…

C# 方法的重载(Overload)

在C#中&#xff0c;方法的重载&#xff08;Overloading&#xff09;是指在一个类中可以有多个同名的方法&#xff0c;只要这些方法具有不同的方法签名&#xff08;即参数的数量、类型或顺序不同&#xff09;。这使得你可以使用相同的方法名称来执行相似但参数不同的操作&#x…

GEE必须会教程——基于Landsat影像构建NDVI时间序列

很久很久以前&#xff0c;小编写了一篇基于MODIS影像构建归一化植被指数的文章&#xff0c;不知道大家还有没有印象&#xff0c;有一段时间没有更新时间序列分析相关的文章了。 今天&#xff0c;我们来看看基于Lansat影像&#xff0c;我们来看看在GEE上如何构建NDVI的时间序列。…

AI跟踪报道第50期-新加坡内哥谈技术-本周AI新闻: 听听没有Scarlett Johansson的GPT-4o更加震撼

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

shellcode加密免杀

通过加密shellcode方式过安全软件拦截 先说结论&#xff0c;笔者没成功 shellcode&#xff1a; Shellcode 是一段用于在目标系统上执行特定操作的机器码。它通常被用于利用软件漏洞&#xff0c;以获取对目标系统的控制权或执行特定的恶意行为。 Shellcode 可以执行诸如创建进程…

MySQL 预处理、如何在 [Node.js] 中使用 MySQL?

前面文章我们已经总结了mysql下载安装配置启动以及如何用 Navicat 连接&#xff0c;还有MySQL的入门基础知识 、Node.js的基本知识、Express框架基于Node.js基础知识、下面我们总结如何在Node.js中使用MySQL数据库以及MySQL预处理基本知识。 目录 一、MySQL预处理 二、如何在…

JavaFX布局-GridPane

JavaFX布局-GridPane 常用实行alignmenthgapvgappaddinggridLinesVisible 实现方式Java实现fxml实现 使用行和列来组织其子节点将节点放置在二维网格中的任何单元格&#xff0c;同时也可以设置跨越行、跨越列 常用实行 alignment 对齐方式&#xff0c;设置内容居中&#xff0…

032-GeoGebra中级篇-列表与集合(list and set)及常用操作大全

列表在 GeoGebra 中扮演着重要角色&#xff0c;使用户能够处理和管理一组数值、点或对象。这些列表可以用于执行多种操作&#xff0c;如计算平均值、排序、查找最大或最小值、绘制点的集合等。通过使用列表&#xff0c;用户可以简化复杂的计算步骤&#xff0c;增强图形的动态性…

《计算机组成原理》(第3版)第1章 计算机系统概论 复习笔记

第1章 计算机系统概论 一、计算机系统简介 &#xff08;一&#xff09;计算机的软硬件概念 1&#xff0e;计算机系统由“硬件”和“软件”两大部分组成 &#xff08;1&#xff09;所谓“硬件”&#xff0c;是指计算机的实体部分&#xff0c;如主机、外部设备等。 &#xff0…

phpMyAdmin 漏洞

一、日志文件拿shell 在sql语句执行界面执行命令 将日志功能打开 再次查看 更改日志保存路径 擦看是否更改成功 植入一句话木马 访问木马 使用工具连接 二、导⼊导出拿WebShell 判断网站位置 判断在www在Extensions同级 写shell 访问shell,使用工具连接 三、可视化界面getshe…

C++11特性(二)

系列文章目录 C11特性(一) 文章目录 系列文章目录前言一、可变模板参数1.1 什么是可变参数模板1.2 如何打印可变模板参数的内容递归函数方式展开参数包逗号表达式展开参数包 1.3 emplace_back的实现1.4 可变模板参数为何高效 二、lambda表达式2.1 C98中的一个例子2.2 lambda表…

基于JAVA的商品供应管理系统-JavaEE

点击下载源码 基于JAVA的商品供应管理系统-JavaEE 摘 要 当今社会己进入信息社会时代&#xff0c;信息己经受到社会的广泛关注&#xff0c;被看作社会和科学技术发展的三大支柱&#xff08;材料、能源、信息&#xff09;之一。信息是管理的基础&#xff0c;是进行决策的基本依…

vue3使用svg(无废话版)

1.去阿里云矢量图标库&#xff0c;复制svg代码 2.新建一个phone.svg文件(存放在assets/icons/phone.svg)&#xff0c;内容是刚刚复制的svg代码 <svg t"1722592653925" class"icon" viewBox"0 0 1024 1024" version"1.1" xmlns&quo…

数据失踪?这四款U盘数据恢复利器教你如何避免错误操作与保障安全性

当U盘上的数据不见了&#xff0c;数据恢复软件就像是你的救命稻草。一些好的数据恢复软件还会提供安全的恢复选项&#xff0c;确保在恢复的过程中不会对原来的数据造成损害。接下来&#xff0c;我们就来看看这些顶级的U盘数据恢复软件是怎么帮我们恢复U盘数据的&#xff0c;同时…

红旗E-QM5起火,一汽红旗否认车辆质量问题

近日&#xff0c;据媒体报道&#xff0c;7月31日下午&#xff0c;长春一辆一汽红旗E-QM5发生起火事故。 一汽红旗方面则表示&#xff1a;“现场勘查和初步调查表明&#xff0c;该事件并非因车辆自身质量问题导致自燃。疑似车辆在行驶过程中与路面井盖发生碰撞导致动力电池受损&…

专业130+总分430+浙大浙江大学842考研信号系统与数字电路经验电子信息与通信工程真题,大纲,参考书。

通过接近一年的备考&#xff0c;专业842信号和数电总结130&#xff08;专业课比预期低&#xff09;&#xff0c;总分430如愿上岸浙大&#xff0c;这一路复习走过弯路&#xff0c;淋过雨&#xff0c;也走过大路&#xff0c;风和日丽&#xff0c;总结一些自己的心得&#xff0c;希…

C语言 ——— 学习、使用 strcmp函数 并模拟实现

目录 strcmp函数的功能 学习strcmp函数​编辑 使用strcmp函数 模拟实现strcmp函数 strcmp函数的功能 strcmp函数的功能是字符串比较&#xff0c;两个字符串的对应位置的字符进行比较&#xff0c;直到字符不同或达到终止的 \0 字符为止 举例说明&#xff1a; 字符串1&am…

法制史学习笔记(个人向) Part.7

法制史学习笔记(个人向) Part.7 11. 清朝法律制度 11.1 立法概述 11.1.1 立法指导思想 简单来说是&#xff1a;详译明律&#xff0c;参以国制&#xff1b; 努尔哈赤时期&#xff0c;后金政权处于由习惯法到成文法的过渡过程中&#xff1b;皇太极统治时期&#xff0c;奉行“参…