GD32 波形发生器,三角波,正弦波,方波。AD9833+MCP410生成和MCU自身的DAC生成。波形,频率,振幅可以通过按键和OLED调整输出。
简单的信号发生器
DIY一个简易的信号发生器驱动板,主要是三角波和正弦波,方波。主板有两个通道能输出波形,
CH0由AD9833+MCP410+AD8051放大电路组成,理论可以生成0.1-12.5MHZ的频率信号,单电源振幅范围是1-9V。
CH1由MCU外设DAC生成的信号,和放大电路组成,DAC可以生成1-100khz的三角波,正弦波,方波,赋值0-3.3v, 可以用运算放大电路放大。
硬件:
- MCU :GD32F303CBT6
- DAC输出通道0 :AD9833+MCP41010+SPI
- DAC输出通道1:MCU外设DAC
- OLED
- 按键
- LED.
2.波形输出通道0,由AD9833+MCP410+AD8051运放组成。
电路图
AD9833和MCP41010都是SPI驱动,大多数教程是用的软件SPI,本次多增加了硬件SPI驱动方式。
软件SPI
#ifndef __GD_SPI_SOF_H_
#define __GD_SPI_SOF_H_
#include "gd32f30x.h"
#include "./gd_gpio/gd_gpio.h"
//软件模拟SPI
typedef struct
{
uint32_t gpio_periph;
uint32_t pin;
}SpiSofGpio;
typedef enum
{
CPOL0_CPHA0,
CPOL0_CPHA1,
CPOL1_CPHA0,
CPOL1_CPHA1,
}SpiSofClockPolarity;//时钟极性
typedef struct
{
SpiSofGpio sck;//引脚
SpiSofGpio mosi;//引脚
SpiSofClockPolarity clock_polarity;//时钟极性
}SpiSofInfo;
typedef enum
{
SPI_SOF_ID0 = 0,
SPI_SOF_ID_MAX
}SpiSofId;
void spi_sof_write_u8(SpiSofId id,uint8_t data);
void spi_sof_write_u16(SpiSofId id,uint16_t data);
void spi_sof_write_buff(SpiSofId id,uint8_t *buff,uint16_t len);
uint8_t spi_sof_clock_polarity_config(SpiSofId id , SpiSofClockPolarity clockp);
void spi_sof_cs_set_enbale(uint32_t gpio_periph ,uint32_t pin,uint8_t enable);
void spi_sof_cs_init(uint32_t gpio_periph ,uint32_t pin);
uint8_t spi_sof_init(SpiSofId id );
#include "gd_spi_sof.h"
#include "./gd_dwt/gd_dwt.h"
#include "am_gpio_config.h"
#define SPI_SOF_GPIO_INIT(g,m,s,p) gd_gpio_config(g,m,s,p)
//#define SPI_SOF_CS_Write(g,p,x) gpio_bit_write(g,p,x)
#define SPI_SOF_CLK_Write(g,p,x) gpio_bit_write(g,p,x)
#define SPI_SOF_MOSI_Write(g,p,x) gpio_bit_write(g,p,x)
static SpiSofInfo spi_sof_buff[SPI_SOF_ID_MAX]={
{SPI_SOF0_SCK_GPIOx,SPI_SOF0_SCK_GPIOx_PINx,SPI_SOF0_MOSI_GPIOx,SPI_SOF0_MOSI_GPIOx_PINx,CPOL0_CPHA1},
};
static void spi_sof_delay(void)
{
uint16_t n=0;
for (n = 0; n < 1; n++);
}
static void spi_sof_cpol0cpha0_write_byte(SpiSofId id,uint8_t byte)
{
uint8_t i=0;
if(id >= SPI_SOF_ID_MAX ){return;}
// 从高位到低位发送(SPI通常默认MSB先行)
for ( i = 0; i < 8; i++) {
SPI_SOF_CLK_Write(spi_sof_buff[id].sck.gpio_periph,spi_sof_buff[id].sck.pin,0);
spi_sof_delay(); // 维持时钟低电平
// 1. 准备数据(在时钟上升沿前设置好MOSI电平)
if (byte & (0x80 >> i)) { // 提取当前位(从bit7到bit0)
SPI_SOF_MOSI_Write(spi_sof_buff[id].mosi.gpio_periph,spi_sof_buff[id].mosi.pin, 1); // 输出高电平
} else {
SPI_SOF_MOSI_Write(spi_sof_buff[id].mosi.gpio_periph,spi_sof_buff[id].mosi.pin, 0); // 输出高电平
}
spi_sof_delay(); // 确保数据稳定
// 2. 产生时钟上升沿(从低到高),此时从机采样数据
SPI_SOF_CLK_Write(spi_sof_buff[id].sck.gpio_periph,spi_sof_buff[id].sck.pin, 1);
spi_sof_delay(); // 维持时钟高电平,保证采样完成
}
SPI_SOF_CLK_Write(spi_sof_buff[id].sck.gpio_periph,spi_sof_buff[id].sck.pin,0);
}
static void spi_sof_cpol0cpha1_write_byte(SpiSofId id,uint8_t byte)
{
uint8_t i=0;
// 从高位到低位发送(MSB先行)
SPI_SOF_CLK_Write(spi_sof_buff[id].sck.gpio_periph, spi_sof_buff[id].sck.pin, 0);
spi_sof_delay();
for ( i = 0; i < 8; i++) {
SPI_SOF_CLK_Write(spi_sof_buff[id].sck.gpio_periph,spi_sof_buff[id].sck.pin, 1);
spi_sof_delay();
if (byte & (0x80 >> i)) { // 提取当前位(从bit7到bit0)
SPI_SOF_MOSI_Write(spi_sof_buff[id].mosi.gpio_periph,spi_sof_buff[id].mosi.pin, 1); // 输出高电平
} else {
SPI_SOF_MOSI_Write(spi_sof_buff[id].mosi.gpio_periph,spi_sof_buff[id].mosi.pin, 0); // 输出低电平
}
spi_sof_delay();
// 3. 产生时钟下降沿(从高到低),此时从机采样数据
SPI_SOF_CLK_Write(spi_sof_buff[id].sck.gpio_periph,spi_sof_buff[id].sck.pin, 0);
spi_sof_delay();
}
// SPI_SOF_CLK_Write(spi_sof_buff[id].sck.gpio_periph,spi_sof_buff[id].sck.pin, 0);
}
static void spi_sof_cpol1cpha0_write_byte(SpiSofId id,uint8_t byte)
{
uint8_t i=0;
// 从高位到低位发送(MSB先行)
for ( i = 0; i < 8; i++) {
SPI_SOF_CLK_Write(spi_sof_buff[id].sck.gpio_periph,spi_sof_buff[id].sck.pin,1);
spi_sof_delay();
if (byte & (0x80 >> i)) { // 提取当前位(从bit7到bit0)
SPI_SOF_MOSI_Write(spi_sof_buff[id].mosi.gpio_periph,spi_sof_buff[id].mosi.pin, 1); // 输出高电平
} else {
SPI_SOF_MOSI_Write(spi_sof_buff[id].mosi.gpio_periph,spi_sof_buff[id].mosi.pin, 0); // 输出低电平
}
spi_sof_delay(); // 确保数据稳定
SPI_SOF_CLK_Write(spi_sof_buff[id].sck.gpio_periph,spi_sof_buff[id].sck.pin, 0);
spi_sof_delay();
}
// 发送完成后,确保时钟回到空闲高电平
SPI_SOF_CLK_Write(spi_sof_buff[id].sck.gpio_periph,spi_sof_buff[id].sck.pin, 1);
}
static void spi_sof_cpol1cpha1_write_byte(SpiSofId id,uint8_t byte)
{
uint8_t i=0;
// 从高位到低位发送(MSB先行)
for ( i = 0; i < 8; i++) {
// 1. 产生时钟下降沿(从高到低)
SPI_SOF_CLK_Write(spi_sof_buff[id].sck.gpio_periph,spi_sof_buff[id].sck.pin,0);
spi_sof_delay();
// 2. 准备数据(在下降沿前设置好MOSI电平)
if (byte & (0x80 >> i)) { // 提取当前位(从bit7到bit0)
SPI_SOF_MOSI_Write(spi_sof_buff[id].mosi.gpio_periph,spi_sof_buff[id].mosi.pin, 1); // 输出高电平
} else {
SPI_SOF_MOSI_Write(spi_sof_buff[id].mosi.gpio_periph,spi_sof_buff[id].mosi.pin, 0); // 输出低电平
}
spi_sof_delay();
// 3. 产生时钟上升沿(从低到高),为下一次采样做准备
SPI_SOF_CLK_Write(spi_sof_buff[id].sck.gpio_periph,spi_sof_buff[id].sck.pin,1);
spi_sof_delay();
}
// 发送完成后,确保时钟回到空闲高电平
SPI_SOF_CLK_Write(spi_sof_buff[id].sck.gpio_periph,spi_sof_buff[id].sck.pin,1);
}
void spi_sof_write_u8(SpiSofId id,uint8_t data)
{
SpiSofClockPolarity clocktype;
clocktype = spi_sof_buff[id].clock_polarity;
switch (clocktype)
{
case CPOL0_CPHA0:
spi_sof_cpol0cpha0_write_byte(id,data);
break;
case CPOL0_CPHA1:
spi_sof_cpol0cpha1_write_byte(id,data);
break;
case CPOL1_CPHA0:
spi_sof_cpol1cpha0_write_byte(id,data);
break;
case CPOL1_CPHA1:
spi_sof_cpol1cpha1_write_byte(id,data);
break;
default:
break;
}
}
void spi_sof_write_u16(SpiSofId id,uint16_t data)
{
spi_sof_write_u8(id,(uint8_t)(data >> 8));
spi_sof_write_u8(id,(uint8_t)(data & 0xff));
//printf("data=%x",data);
}
void spi_sof_write_buff(SpiSofId id,uint8_t *buff,uint16_t len)
{
uint16_t i=0;
for(i=0;i<len;i++)
{
spi_sof_write_u8(id,buff[i]);
}
}
uint8_t spi_sof_clock_polarity_config(SpiSofId id , SpiSofClockPolarity clockp)
{
if(id >= SPI_SOF_ID_MAX ){
return 0;}
spi_sof_buff[id].clock_polarity = clockp;
if(clockp == CPOL0_CPHA0 || clockp == CPOL0_CPHA1){
SPI_SOF_CLK_Write(spi_sof_buff[id].sck.gpio_periph,spi_sof_buff[id].sck.pin,0);
}else
{
SPI_SOF_CLK_Write(spi_sof_buff[id].sck.gpio_periph,spi_sof_buff[id].sck.pin,1);
}
return 1;
}
uint8_t spi_sof_init(SpiSofId id )
{
if(id >= SPI_SOF_ID_MAX ){
return 0;}
SPI_SOF_GPIO_INIT(spi_sof_buff[id].sck.gpio_periph,
GPIO_MODE_OUT_PP ,
GPIO_OSPEED_50MHZ,
spi_sof_buff[id].sck.pin);
SPI_SOF_GPIO_INIT(spi_sof_buff[id].mosi.gpio_periph,
GPIO_MODE_OUT_PP ,
GPIO_OSPEED_50MHZ,
spi_sof_buff[id].mosi.pin);
return 1;
}
void spi_sof_cs_init(uint32_t gpio_periph ,uint32_t pin)
{
gd_gpio_config(gpio_periph,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,pin);
}
void spi_sof_cs_set_enbale(uint32_t gpio_periph ,uint32_t pin,uint8_t enable)
{
if(enable){
GD_SET_PIN_L(gpio_periph,pin);
}else{
GD_SET_PIN_H(gpio_periph,pin);
}
}
硬件SPI配置
#ifndef __GD_SPI_H_
#define __GD_SPI_H_
#include "gd32f30x.h"
void gd_spi_init(uint32_t spi_periph ,uint8_t spi_remap_t);
void gd_spi_css_soft_gpio_init(uint32_t gpio_periph ,uint32_t pin);
void gd_spi_css_soft_set_enbale(uint32_t gpio_periph ,uint32_t pin,uint8_t enable);
#if 1
uint8_t gd_spi_transmit_u8buff(uint32_t spi_periph, uint8_t *pData, uint16_t Size, uint32_t Timeout);
//uint8_t gd_spi_transmit_u16buff(uint32_t spi_periph, uint16_t *pData, uint16_t Size, uint32_t Timeout);
uint8_t gd_spi_receive_u8buff(uint32_t spi_periph, uint8_t *pData, uint16_t Size, uint32_t Timeout);
uint8_t gd_spi_transmit_receive(uint32_t spi_periph,
const uint8_t *tx_buf,
uint8_t *rx_buf,
uint16_t size,
uint32_t Timeout);
#endif
#include "gd_spi.h"
#include "./gd_gpio/gd_gpio.h"
#include "./gd_system/gd_system.h"
#include "./gd_dwt/gd_dwt.h"
#include <stdio.h>
#define SPI_GET_TICK() gd_system_get_tick()
#define SPI_Delayus(s) DWT_DelayUS(s)
void gd_spi_css_soft_gpio_init(uint32_t gpio_periph ,uint32_t pin)
{
gd_gpio_config(gpio_periph,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,pin);
}
void gd_spi_css_soft_set_enbale(uint32_t gpio_periph ,uint32_t pin,uint8_t enable)
{
if(enable){
GD_SET_PIN_L(gpio_periph,pin);
}else{
GD_SET_PIN_H(gpio_periph,pin);
}
}
//默认spi引脚
static void gd_spi_gpio_config(uint32_t spi_periph)
{
if(spi_periph == SPI0)
{
//SPI0 SCL PA5
gd_gpio_config(GPIOA,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_5);
//SPI0 MOSI PA7
gd_gpio_config(GPIOA,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_7);
//SPI0 MISO PA6
gd_gpio_config(GPIOA,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,GPIO_PIN_6);
}else if(spi_periph == SPI1)
{
//SPI1 SCL PB13
gd_gpio_config(GPIOB,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_13);
//SPI1 MOSI PB15
gd_gpio_config(GPIOB,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_15);
//SPI1 MISO PB14
gd_gpio_config(GPIOB,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,GPIO_PIN_14);
}
else if(spi_periph == SPI2)
{
//PB3,PB4特殊引脚。
//gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP,ENABLE);
//SPI2 SCL PB3
gd_gpio_config(GPIOB,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_3);
//SPI2 MOSI PB5
gd_gpio_config(GPIOB,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_5);
//SPI2 MISO PB4
gd_gpio_config(GPIOB,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,GPIO_PIN_4);
}
else
{
return ;
}
}
//复用spi引脚初始化
static void gd_spi_remap_gpio_config(uint32_t spi_periph)
{
if(spi_periph == SPI0)
{
gpio_pin_remap_config(GPIO_SPI0_REMAP, ENABLE);//SPI0引脚重映射
//SPI0 SCL PB3
gd_gpio_config(GPIOB,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_3);
//SPI0 MOSI PB5
gd_gpio_config(GPIOB,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_5);
//SPI0 MISO PB4
gd_gpio_config(GPIOB,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,GPIO_PIN_4);
}else if(spi_periph == SPI1)
{
return;
}
else if(spi_periph == SPI2)
{
gpio_pin_remap_config(GPIO_SPI2_REMAP, ENABLE);
//PB3,PB4特殊引脚。
//gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP,ENABLE);
//SPI2 SCL PC10
gd_gpio_config(GPIOC,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_10);
//SPI2 MOSI PC12
gd_gpio_config(GPIOC,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_12);
//SPI2 MISO PC11
gd_gpio_config(GPIOC,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,GPIO_PIN_11);
}
else
{
return ;
}
}
void gd_spi_init(uint32_t spi_periph ,uint8_t spi_remap_t)
{
spi_parameter_struct spi_init_struct;
rcu_periph_clock_enable(RCU_AF);
if(spi_remap_t == 0){
gd_spi_gpio_config(spi_periph);
}
else
{
if(spi_periph == SPI1){
return ;
}
gd_spi_remap_gpio_config(spi_periph);
}
if(spi_periph == SPI0){
rcu_periph_clock_enable(RCU_SPI0);
}else if(spi_periph == SPI1)
{
rcu_periph_clock_enable(RCU_SPI1);
}else if(spi_periph == SPI2)
{
rcu_periph_clock_enable(RCU_SPI2);
}else{return;}
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;//传输模式 全双工
spi_init_struct.device_mode = SPI_MASTER;//设备模式 主机模式
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT;//8位字节
spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_1EDGE;//时钟极性和相位
spi_init_struct.nss = SPI_NSS_SOFT;//片选模式 软件片选
spi_init_struct.prescale = SPI_PSC_256;//分配系数
spi_init_struct.endian = SPI_ENDIAN_MSB;//字节顺序 大端
spi_init(spi_periph, &spi_init_struct);
spi_enable(spi_periph);
}
//轮询堵塞的方式
#if 1
uint8_t gd_spi_transmit_u8buff(uint32_t spi_periph, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint8_t i;
uint32_t tickstart;
uint32_t tick_cnt;
if(pData == NULL || Size == 0){return 0;}
tickstart = SPI_GET_TICK();
for(i=0;i < Size; i++)
{
while (RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TBE)) {
if((SPI_GET_TICK() - tickstart) > Timeout){
return 0;
}
}
spi_i2s_data_transmit(spi_periph, pData[i]);
if(SPI_GET_TICK() < tickstart) {
tick_cnt = (0xFFFFFFFF - tickstart) + SPI_GET_TICK() + 1;
}
else{
tick_cnt = SPI_GET_TICK() - tickstart;
}
if(tick_cnt > Timeout){return 0;}
}
// 等待最后一个字节完全发送完成
while (spi_i2s_flag_get(spi_periph, SPI_FLAG_TRANS)) {
if((SPI_GET_TICK() - tickstart) > Timeout){
return 0; // 超时错误
}
}
return 1;
}
uint8_t gd_spi_transmit_u16buff(uint32_t spi_periph, uint16_t *pData, uint16_t Size, uint32_t Timeout)
{
uint8_t i;
uint32_t tickstart;
uint32_t tick_cnt;
if(pData == NULL || Size == 0){return 0;}
tickstart = SPI_GET_TICK();
for(i=0;i < Size; i++)
{
while (RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TBE)) {
if((SPI_GET_TICK() - tickstart) > Timeout){
return 0;
}
}
spi_i2s_data_transmit(spi_periph, pData[i]);
if(SPI_GET_TICK() < tickstart) {
tick_cnt = (0xFFFFFFFF - tickstart) + SPI_GET_TICK() + 1;
}
else{
tick_cnt = SPI_GET_TICK() - tickstart;
}
if(tick_cnt > Timeout){return 0;}
}
// 等待最后一个字节完全发送完成
while (spi_i2s_flag_get(spi_periph, SPI_FLAG_TRANS)) {
if((SPI_GET_TICK() - tickstart) > Timeout){
return 0; // 超时错误
}
}
return 1;
}
uint8_t gd_spi_receive_u8buff(uint32_t spi_periph, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint8_t i;
uint32_t tickstart;
uint32_t tick_cnt;
if(pData == NULL || Size == 0){return 0;}
tickstart = SPI_GET_TICK();
for(i =0; i < Size; i++){
while (RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RBNE)) {
if((SPI_GET_TICK() - tickstart) > Timeout){
return 0;
}
}
if(SPI_GET_TICK() < tickstart) {
tick_cnt = (0xFFFFFFFF - tickstart) + SPI_GET_TICK() + 1;
}
else{
tick_cnt = SPI_GET_TICK() - tickstart;
}
if(tick_cnt > Timeout){return 0;}
pData[i] = (uint8_t)spi_i2s_data_receive(spi_periph);
}
return 1;
}
/**
* @brief SPI全双工通信函数,同时发送和接收数据
* @param spi_periph: SPI外设 (SPI0, SPI1, SPI2等)
* @param tx_buf: 发送数据缓冲区指针,NULL表示只接收不发送
* @param rx_buf: 接收数据缓冲区指针,NULL表示只发送不接收
* @param size: 要发送/接收的数据长度
* @param timeout: 超时时间(单位:滴答数)
* @return 0: 超时错误, 1: 成功, 0xFF: 参数错误
*/
uint8_t gd_spi_transmit_receive(uint32_t spi_periph,
const uint8_t *tx_buf,
uint8_t *rx_buf,
uint16_t size,
uint32_t Timeout)
{
uint16_t i;
uint32_t tickstart;
uint16_t tx_data, rx_data;
uint32_t tick_cnt;
// 参数合法性检查
if (size == 0 || (tx_buf == NULL && rx_buf == NULL)) {
return 0; // 参数错误
}
tickstart = SPI_GET_TICK();
for (i = 0; i < size; i++) {
if(SPI_GET_TICK() < tickstart) {
tick_cnt = (0xFFFFFFFF - tickstart) + SPI_GET_TICK() + 1;
}
else{
tick_cnt = SPI_GET_TICK() - tickstart;
}
if(tick_cnt > Timeout){return 0;}
// 准备要发送的数据,如果发送缓冲区为空则发送0xFF
tx_data = (tx_buf != NULL) ? tx_buf[i] : 0xFF;
// 等待发送缓冲区为空
while (spi_i2s_flag_get(spi_periph, SPI_FLAG_TBE) == RESET) {
if ((SPI_GET_TICK() - tickstart) > Timeout) {
return 0; // 超时错误
}
}
// 发送数据
spi_i2s_data_transmit(spi_periph, tx_data);
// 等待接收缓冲区有数据
while (spi_i2s_flag_get(spi_periph, SPI_FLAG_RBNE) == RESET) {
if ((SPI_GET_TICK() - tickstart) > Timeout) {
return 0; // 超时错误
}
}
// 读取接收的数据
rx_data = spi_i2s_data_receive(spi_periph);
// 如果接收缓冲区不为空,则存储接收的数据
if (rx_buf != NULL) {
rx_buf[i] = (uint8_t)rx_data;
}
}
return 1; // 操作成功
}
#endif
软件SPI和硬件SPI,使用软件SPI的好处是使用和移植简单,随便的单片机换一下IO引脚就能用,硬件的话还需要根据手册来配置。
关于AD9833驱动的一些驱动注意事项。
我使用的晶振是25MHZ,所以AD9833输出的频率最大12.5M.VCC用的3.3V.单电源,
1.那么输出的正弦波和三角波的最大电压是0.6V,但是我用示波器测得是0.64V左右。
2.输出方波时是VCC的电压。
3.SPI的配置需要正确,时钟频率不需要太高,1M就够了,时钟极性 CPOL=1, CPHA=0,不然通讯不了。
AD9833驱动代码。
#ifndef __AD9833_H
#define __AD9833_H
#include "./gd_spi/gd_spi.h"
typedef enum
{
DAC_SINE_TYPE,//正弦波
DAC_TRIANGLE_TYPE,//三角波
DAC_PWM_MSB_2_TYPE, //方波MSB/2
DAC_PWM_MSB_TYPE//方波MSB
}AD9833OutModeType;
typedef enum
{
FREQ0,
FREQ1,
}AD9833_FreqType;
typedef enum
{
PHASE0,
PHASE1,
}AD9833_PhaseType;
#define AD9833_BIT(x) ((uint32_t)((uint32_t)0x01U<<(x)))
#define AD9833_SET_BIT(data, bit) ((data) |= (bit))
#define AD9833_CLEAR_BIT(data, bit) ((data) &= ~(bit))
/* 寄存器 */
#define AD9833_REG_CMD (0 << 14)// 0000 0000 0000 0000 寄存器即将更新
#define AD9833_REG_FREQ0_CMD (1 << 14)// 0100 0000 0000 0000 频率寄存器0
#define AD9833_REG_FREQ1_CMD (2 << 14)// 1000 0000 0000 0000 频率寄存器0
#define AD9833_REG_PHASE0_CMD (6 << 13)// 1100 0000 0000 0000 写入相位寄存器0
#define AD9833_REG_PHASE1_CMD (7 << 13)// 1110 0000 0000 0000 写入相位寄存器1
#define AD9833_B28 AD9833_BIT(13)
#define AD9833_HLB AD9833_BIT(12)
#define AD9833_FSELECT AD9833_BIT(11)
#define AD9833_PSELECT AD9833_BIT(10)
#define AD9833_RESERVED_D9 AD9833_BIT(9)//清零
#define AD9833_RESET AD9833_BIT(8) //复位
#define AD9833_SLEEP1 AD9833_BIT(7)
#define AD9833_SLEEP12 AD9833_BIT(6)
#define AD9833_OPBITEN AD9833_BIT(5)//
#define AD9833_RESERVED_D4 AD9833_BIT(4) //清零
#define AD9833_DIV2 AD9833_BIT(3)
#define AD9833_RESERVED_D2 AD9833_BIT(2) //清零
#define AD9833_MODE AD9833_BIT(1)
#define AD9833_RESERVED_D0 AD9833_BIT(0)////清零
#define F_MCLK_HZ 25000000 //主频25M
#define OUT_MAX_FREQ_HZ F_MCLK_HZ/2 //输出最大频率为时钟的一半
#define AD9833_Ki 268435456 //分辨率28位 2的28次方
#define AD9833_OUT_TRIANGLE_SINE_AMPLITUDE_MAX 630//三角波和弦波是0.64V
#define AD9833_OUT_PWM_AMPLITUDE_MAX 3300// pwm 波3.3v
void ad9833_init(void);
void ad9833_reset(void);
void ad9833_config_freq(AD9833_FreqType type,uint32_t freq);
void ad9833_config_phase(AD9833_PhaseType type,uint16_t phase_val);
void ad9833_config_mode(AD9833_FreqType freqx,AD9833_PhaseType phasex,AD9833OutModeType type);
#include "ad9833.h"
#include "./gd_gpio/gd_gpio.h"
#include "am_gpio_config.h"
#include "./gd_dwt/gd_dwt.h"
#include "./gd_spi/gd_spi_sof.h"
typedef struct
{
uint32_t gpio_periph;
uint32_t pin;
}Ad9833CsGpio;
#define AD9833_SPIx SPI1
#define AD9833_Dealy_ms(x) DWT_DelayMS(x)
static Ad9833CsGpio ad9833_cs_gpio={AD9833_CS_GPIOx,AD9833_CS_GPIOx_PINx};
static void ad9833_cs_set_enable(uint8_t enable)
{
if(enable)
GD_SET_PIN_L(ad9833_cs_gpio.gpio_periph,ad9833_cs_gpio.pin);
else
GD_SET_PIN_H(ad9833_cs_gpio.gpio_periph,ad9833_cs_gpio.pin);
}
static void ad9833_write_data(uint16_t data)
{
uint8_t txbuff[2];
txbuff[0] = (uint8_t)(data >> 8);
txbuff[1] = (uint8_t)(data&0xff);
ad9833_cs_set_enable(1);
//gd_spi_transmit_u8buff(SPI1,txbuff,2,5);
spi_sof_write_u16(SPI_SOF_ID0,data);
ad9833_cs_set_enable(0);
}
void ad9833_WaveSeting(double Freq,unsigned int Freq_SFR,unsigned int WaveMode,unsigned int Phase )
{
int frequence_LSB,frequence_MSB,Phs_data;
double frequence_mid,frequence_DATA;
long int frequence_hex;
/*********************************计算频率的16进制值***********************************/
frequence_mid=268435456/25;//适合25M晶振
//如果时钟频率不为25MHZ,修改该处的频率值,单位MHz ,AD9833最大支持25MHz
frequence_DATA=Freq;
frequence_DATA=frequence_DATA/1000000;
frequence_DATA=frequence_DATA*frequence_mid;
frequence_hex=frequence_DATA; //这个frequence_hex的值是32位的一个很大的数字,需要拆分成两个14位进行处理;
frequence_LSB=frequence_hex; //frequence_hex低16位送给frequence_LSB
frequence_LSB=frequence_LSB&0x3fff;//去除最高两位,16位数换去掉高位后变成了14位
frequence_MSB=frequence_hex>>14; //frequence_hex高16位送给frequence_HSB
frequence_MSB=frequence_MSB&0x3fff;//去除最高两位,16位数换去掉高位后变成了14位
Phs_data=Phase|0xC000; //相位值
ad9833_write_data(0x0100); //复位AD9833,即RESET位为1
ad9833_write_data(0x2100); //选择数据一次写入,B28位和RESET位为1
if(Freq_SFR==0) //把数据设置到设置频率寄存器0
{
frequence_LSB=frequence_LSB|0x4000;
frequence_MSB=frequence_MSB|0x4000;
//使用频率寄存器0输出波形
ad9833_write_data(frequence_LSB); //L14,选择频率寄存器0的低14位数据输入
ad9833_write_data(frequence_MSB); //H14 频率寄存器的高14位数据输入
ad9833_write_data(Phs_data); //设置相位
//AD9833_Write(0x2000); /**设置FSELECT位为0,芯片进入工作状态,频率寄存器0输出波形**/
}
if(Freq_SFR==1) //把数据设置到设置频率寄存器1
{
frequence_LSB=frequence_LSB|0x8000;
frequence_MSB=frequence_MSB|0x8000;
//使用频率寄存器1输出波形
ad9833_write_data(frequence_LSB); //L14,选择频率寄存器1的低14位输入
ad9833_write_data(frequence_MSB); //H14 频率寄存器1为
ad9833_write_data(Phs_data); //设置相位
//AD9833_Write(0x2800); /**设置FSELECT位为0,设置FSELECT位为1,即使用频率寄存器1的值,芯片进入工作状态,频率寄存器1输出波形**/
}
if(WaveMode==0) //输出三角波波形
ad9833_write_data(0x2002);
if(WaveMode==2) //输出方波波形
ad9833_write_data(0x2028);
if(WaveMode==1) //输出正弦波形
ad9833_write_data(0x2000);
}
#if 1
void ad9833_reset(void)
{
uint16_t txdata = 0;
txdata |= AD9833_REG_CMD ;
AD9833_SET_BIT(txdata,AD9833_RESET);
printf("reset=%x\r\n",txdata);
ad9833_write_data(txdata);
}
void ad9833_config_mode(AD9833_FreqType freqx,AD9833_PhaseType phasex,AD9833OutModeType type)
{
uint16_t txdata = 0;
txdata |= AD9833_REG_CMD ;
if(type == DAC_SINE_TYPE){
AD9833_CLEAR_BIT(txdata,AD9833_OPBITEN);
AD9833_CLEAR_BIT(txdata,AD9833_MODE);
}
else if(type == DAC_TRIANGLE_TYPE)
{
AD9833_CLEAR_BIT(txdata,AD9833_OPBITEN);
AD9833_SET_BIT(txdata,AD9833_MODE);
}else if(type == DAC_PWM_MSB_2_TYPE)
{
AD9833_SET_BIT(txdata,AD9833_OPBITEN);
AD9833_CLEAR_BIT(txdata,AD9833_MODE);
AD9833_CLEAR_BIT(txdata,AD9833_DIV2);
}
else if(type == DAC_PWM_MSB_TYPE)
{
AD9833_SET_BIT(txdata,AD9833_OPBITEN);
AD9833_CLEAR_BIT(txdata,AD9833_MODE);
AD9833_SET_BIT(txdata,AD9833_DIV2);
}
else
{
return ;
}
if(freqx == FREQ0){
AD9833_CLEAR_BIT(txdata,AD9833_FSELECT);
}
else
{
AD9833_SET_BIT(txdata,AD9833_FSELECT);
}
if(phasex == PHASE0){
AD9833_CLEAR_BIT(txdata,AD9833_PSELECT);
}
else
{
AD9833_SET_BIT(txdata,AD9833_PSELECT);
}
ad9833_write_data(txdata);
// printf("mode=%x\r\n",txdata);
}
void ad9833_config_freq(AD9833_FreqType type,uint32_t freq)
{
uint16_t freqcmd,lsb,msb;
uint16_t txdata =0 ;
uint32_t mfreq;
uint32_t write_28b;
if(type == FREQ0){
freqcmd = AD9833_REG_FREQ0_CMD;
}else{
freqcmd = AD9833_REG_FREQ1_CMD;
}
if(freq > OUT_MAX_FREQ_HZ)
{
mfreq = OUT_MAX_FREQ_HZ ;
}else {
mfreq = freq;
}
write_28b = (uint32_t)(((double)AD9833_Ki * mfreq / F_MCLK_HZ) + 0.5);
lsb = (uint16_t)(write_28b & 0x3fff);
msb = (uint16_t)(write_28b >>14 );
txdata = 0x0000;
txdata |= AD9833_REG_CMD ;
AD9833_SET_BIT(txdata,AD9833_B28);
AD9833_SET_BIT(txdata,AD9833_RESET);
ad9833_write_data(txdata);//准备更新寄存器
//printf("cmd=%x\r\n",txdata);
txdata = 0x0000;
txdata |= (freqcmd |lsb);
ad9833_write_data(txdata);//写入频率寄存器0的LSB
//printf("lsb=%x\r\n",txdata);
txdata = 0x0000;
txdata |= (freqcmd | msb);
ad9833_write_data(txdata);//写入频率寄存器0的MSB
//printf("msb=%x\r\n",txdata)
}
void ad9833_config_phase(AD9833_PhaseType type,uint16_t phase_val)
{
uint16_t phase_cmd;
uint16_t txdata;
if(type == PHASE0){
phase_cmd = AD9833_REG_PHASE0_CMD;
}else {
phase_cmd = AD9833_REG_PHASE1_CMD;
}
txdata = (phase_cmd|phase_val);
ad9833_write_data(txdata);
}
#endif
void ad9833_init(void)
{
spi_sof_cs_init(ad9833_cs_gpio.gpio_periph, ad9833_cs_gpio.pin);
spi_sof_cs_set_enbale(ad9833_cs_gpio.gpio_periph, ad9833_cs_gpio.pin,0);
ad9833_reset();
ad9833_config_freq(FREQ0,1000000);
ad9833_config_freq(FREQ1,5000);
ad9833_config_phase(PHASE0,0);
ad9833_config_phase(PHASE1,0);
ad9833_config_mode(FREQ0,PHASE0,DAC_PWM_MSB_TYPE);
}
上面的代码是AD9833的封装,只要你的SPI没问题,可以直接使用
static void ad9833_write_data(uint16_t data)
{
uint8_t txbuff[2];
txbuff[0] = (uint8_t)(data >> 8);
txbuff[1] = (uint8_t)(data&0xff);
ad9833_cs_set_enable(1);
//gd_spi_transmit_u8buff(SPI1,txbuff,2,5);
spi_sof_write_u16(SPI_SOF_ID0,data);
ad9833_cs_set_enable(0);
}
这是SPI写入2个字节的,软件SPI用spi_sof_write_u16(SPI_SOF_ID0,data);
硬件SPI用gd_spi_transmit_u8buff(SPI1,txbuff,2,5);
//---------------------------------------------------------------------------------------
关于MCP41010是一个数字电位器,阻值是0-10K,抽头数是256,也就是分辨率。电阻分辨率最低档能调到40Ω,实际做不到40Ω,
MCU41010电位器主要是形成电阻分压电路,把AD9833出来的振幅衰减,MCU41010电位器当上管R1,下管R2采用1K.也就是说可以衰减的范围是0.9-0.09。AD9833正弦波和方波的信号是振幅是0-0.6V,理论上,通过修改MCP41010的阻值,可以输出的电压0.054-0.54V。MCU41010可以调节256个档位调节输出电压。在经过过后记的同比例运放,输出一个范围比较广的振幅了。
但是MCP41010的精度太低,还有的就是和信号的频率有关,会极大的影响电位器的阻值。需要软件校准。
MCP采用的也是SPI通讯,极性和AD9833的一样就行了。
#ifndef __MCP41010_H
#define __MCP41010_H
#include "./gd_spi/gd_spi.h"
typedef struct
{
uint32_t gpio_periph;
uint32_t pin;
}Mcp41010CsGpio;
#define MCP41010_WRITE_DATA_CMD 0x10
#define MCP41010_SHUTOWN_CMD 0x20
#define MCP41010_POTENTIOMETER_0 0x01
#define MCP41010_POTENTIOMETER_1 0x02
#define MCP41010_SizeR 10300//Ω 总电阻
#define MCP41010_SizeD 256-1 //触点,抽头
void mcp41010_set_RAD(uint8_t data);
void mcp41010_set_RA(uint32_t R);
void mcp41010_init(void);
#include "mcp41010.h"
#include "am_gpio_config.h"
#include "./gd_gpio/gd_gpio.h"
#include "./gd_dwt/gd_dwt.h"
#include "./gd_spi/gd_spi_sof.h"
#define MCP41010_SPIx SPI1
#define MCP41010_Dealy_us(x) DWT_DelayUS(x)
static Mcp41010CsGpio mcp41010_cs_gpio={MCP41010_CS_GPIOx,MCP41010_CS_GPIOx_PINx};
static void mcp41010_cs_set_enable(uint8_t enable)
{
if(enable)
GD_SET_PIN_L(mcp41010_cs_gpio.gpio_periph,mcp41010_cs_gpio.pin);
else
GD_SET_PIN_H(mcp41010_cs_gpio.gpio_periph,mcp41010_cs_gpio.pin);
}
static void mcp41010_write_cmd(uint8_t cmd ,uint8_t data)
{
uint8_t txbuff[2];
uint16_t txdata;
txbuff[0] = cmd;
txbuff[1] = data;
txdata = ((cmd<<8) | data);
mcp41010_cs_set_enable(1);
//gd_spi_transmit_u8buff(MCP41010_SPIx,txbuff,2,10);
spi_sof_write_u16(SPI_SOF_ID0,txdata);
mcp41010_cs_set_enable(0);
}
void mcp41010_set_RAD(uint8_t data)
{
if(data > MCP41010_SizeD){
data = MCP41010_SizeD;
}
data = 255-data;
mcp41010_write_cmd(MCP41010_WRITE_DATA_CMD|MCP41010_POTENTIOMETER_0,data);
}
void mcp41010_set_RA(uint32_t R)
{
uint8_t D;
if(R > MCP41010_SizeR){
R = MCP41010_SizeR;
}
D = (256 - (256*R)/MCP41010_SizeR);
if(D > 255){D = 255;}
if(D < 0){D = 0;}
printf("mcp D=%d\r\n",D);
mcp41010_write_cmd(MCP41010_WRITE_DATA_CMD|MCP41010_POTENTIOMETER_0,D);
}
void mcp41010_init(void)
{
gd_gpio_config(mcp41010_cs_gpio.gpio_periph,\
GPIO_MODE_OUT_PP,\
GPIO_OSPEED_50MHZ,\
mcp41010_cs_gpio.pin);
mcp41010_cs_set_enable(0);
}
同向比例运放我放大了19倍
//====================================================================
进一步封装输出的信号。
DacChannelInfo dac_outchannel_info[DAC_OUT_CHANNEL_MAX]=
{
{
.out_state = DAC_OFF,
.out_gain = 19,
.out_type = PWM_TYPE,
.out_freq = 100,
.out_amplitude = 3300,
},
{
.out_state = DAC_OFF,
.out_gain = 3,
.out_type = PWM_TYPE,
.out_freq = 100,
.out_amplitude = 3300,
},
};
void ad9833_dac_out_wave(DacOutState state)
{
DacOutType type;
uint32_t freq,amplitude;
uint32_t val_mv;
uint32_t R1,R2;
R2 = 1000;//分压电阻1K
dac_outchannel_info[AD9833_OUT_DAC].out_state = state;
if(state == DAC_ON)
{
type = dac_outchannel_info[AD9833_OUT_DAC].out_type;
freq = dac_outchannel_info[AD9833_OUT_DAC].out_freq;
amplitude = dac_outchannel_info[AD9833_OUT_DAC].out_amplitude;
val_mv = amplitude/dac_outchannel_info[AD9833_OUT_DAC].out_gain;
if(type == PWM_TYPE){
R1 = ((R2*(AD9833_OUT_PWM_AMPLITUDE_MAX - val_mv)) / val_mv);
mcp41010_set_RA(R1);
}
else
{
R1 = (R2*(AD9833_OUT_TRIANGLE_SINE_AMPLITUDE_MAX - val_mv) / val_mv);
mcp41010_set_RA(R1);
}
ad9833_config_freq(FREQ0,freq);
ad9833_config_phase(PHASE0,0);
printf("ad9833 freq=%d,val_mv=%d,R1=%d\r\n",freq,val_mv,R1);
if(type == TRIANGLE_TYPE){
ad9833_config_mode(FREQ0,PHASE0,DAC_TRIANGLE_TYPE);
}
else if(type == SINE_TYPE){
ad9833_config_mode(FREQ0,PHASE0,DAC_SINE_TYPE);
}else if(type == PWM_TYPE){
//pwm波时是3.2V
ad9833_config_mode(FREQ0,PHASE0,DAC_PWM_MSB_TYPE);
}else{
ad9833_reset();
}
}
else
{
ad9833_reset();
}
}
typedef enum
{
AD9833_OUT_DAC,
MCU_OUT_DAC,
DAC_OUT_CHANNEL_MAX
}DacId;
typedef enum
{
TRIANGLE_TYPE,//三角
SINE_TYPE,//正弦波
PWM_TYPE,//方波
LISF_TYPE, //噪声波
STRAIGHT_TYPE,//直流
}DacOutType;
typedef enum
{
DAC_OFF,
DAC_ON
}DacOutState;
typedef struct
{
DacOutState out_state;
float out_gain;//增益
DacOutType out_type;//类型
uint32_t out_freq; //频率
uint32_t out_amplitude;//振幅
}DacChannelInfo;
.out_type = PWM_TYPE,
.out_freq = 100,
.out_amplitude = 3300,
修改需要输出的参数就可以,最主要的还是输出电压方面的设置。
例如,我需要输出的一个1K,9V的正弦波。
设置AD9833输出正弦波类型,频率是1khz,前面有AD9833驱动有,直接配置就好了,此时输出电压最大是0.6V-0.64V。想要输出到9V的信号,就需要把信号AD9833的信号先衰减到合适的值,然后在经过比例运放电路放大。比例运放是固定的,比如我的19倍。
val_mv = 9V/19=0.47V
R1 = (R2*(AD9833_OUT_TRIANGLE_SINE_AMPLITUDE_MAX - val_mv) / val_mv);
mcp41010_set_RA(R1);
val_mv衰减的信号电压。然后通过串联分压电阻的公式,MCP的电阻要设置 R1=R2(V1-V2)/V1.
R1=(1K(0.64v-0.47V )/0.47V )=360R
MCP410的电阻需要设置到360Ω时,最终才能输出到9V,因为MCP41010的精度太差,设置不到精准的阻值,所以有误差。
AD9833输出信号波形基本完成了。
用示波器看一下输出的波形
电压误差还是有点大的,如果需要精度更高的话,需要用软件补偿,把MCP41010的阻值设置的更精准一些,再试一下2k的正弦波,振幅是0-3V。
目前是幅值越小,电压就越准确。
//========================================================================
二、使用GD32F303自身的DAC输出波形,主要用的模式是DAC+DMA+定时器的方式。
自带的DAC频率适合频率低的时候,比如波形需要越平滑,那么就需要更多的点。这也导致了频率不能太高,不然一个周期的点数不够,就会不平滑。
配置代码如下:
#ifndef _GD_DAC_H
#define _GD_DAC_H
#include "gd32f30x.h"
//DAC_OUT0
//DAC_OUT1
//直流dac
void gd_dac_convert_init(uint8_t dac_out);
void gd_dac_dma_timer_init(uint8_t dac_out,uint32_t timer_periph,uint16_t *txbuff,uint16_t size);
//定时器LFSR噪声
void gd_dac_wave_lfsr_init(uint8_t dac_out ,uint16_t value ,uint32_t unmask_bits ,uint32_t timer_periph);
//定时器三角波
void gd_dac_wave_triangle_init(uint8_t dac_out ,uint16_t value ,uint32_t amplitude,uint32_t timer_periph);
//输出指定dac电压
void gd_dac_set_convert_value(uint8_t dac_out,uint16_t data);
#endif
#include "gd_dac.h"
#include "./gd_gpio/gd_gpio.h"
#define DAC0_R8DH_ADDRESS (0x40007410)
#define DAC0_R12DH_ADDRESS (0x40007408)
static void gd_dac_out_gpio_config(uint8_t dac_out)
{
if(dac_out == DAC_OUT0){
gd_gpio_config(GPIOC, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ,GPIO_PIN_4);
}else if(dac_out == DAC_OUT1){
gd_gpio_config(GPIOC, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ,GPIO_PIN_5);
}
}
static void gd_dac_dma_config(uint8_t dac_out , uint16_t *txbuff,uint32_t size)
{
dma_parameter_struct dma_struct;
dma_channel_enum dma_chx;
rcu_periph_clock_enable(RCU_DMA1);
/* clear all the interrupt flags */
if(dac_out == DAC_OUT0)
{
dma_chx = DMA_CH2;
}else if(dac_out == DAC_OUT0)
{
dma_chx = DMA_CH3;
}
dma_deinit(DMA1,dma_chx);
dma_flag_clear(DMA1, dma_chx, DMA_INTF_GIF);
dma_flag_clear(DMA1, dma_chx, DMA_INTF_FTFIF);
dma_flag_clear(DMA1, dma_chx, DMA_INTF_HTFIF);
dma_flag_clear(DMA1, dma_chx, DMA_INTF_ERRIF);
#if 1
/* configure the DMA1 channel 2 */
dma_struct.periph_addr = (uint32_t)(&DAC_OUT0_R12DH(DAC0));
dma_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;//外设数据宽度
dma_struct.memory_addr = (uint32_t)txbuff;
dma_struct.memory_width = DMA_MEMORY_WIDTH_16BIT; // 内存数据宽度
dma_struct.number = size; //数量
dma_struct.priority = DMA_PRIORITY_ULTRA_HIGH;//传输优先级
dma_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;//外设地址禁用地址增加
dma_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;//内存地址自动增加
dma_struct.direction = DMA_MEMORY_TO_PERIPHERAL;//内存到外设
dma_init(DMA1, dma_chx, &dma_struct);
#endif
/* configure the DMA1 channel 2 */
// dma_struct.periph_addr = DAC0_R12DH_ADDRESS;
// dma_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
// dma_struct.memory_addr = (uint32_t)convertarr16;
// dma_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
// dma_struct.number = CONVERT_NUM;
// dma_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
// dma_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
// dma_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
// dma_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
// dma_init(DMA1, DMA_CH2, &dma_struct);
dma_circulation_enable(DMA1, dma_chx);
dma_channel_enable(DMA1, dma_chx);
}
void gd_dac_convert_init(uint8_t dac_out)
{
rcu_periph_clock_enable(RCU_DAC);
gd_dac_out_gpio_config(dac_out);
/* DAC trigger config */
dac_trigger_source_config(DAC0, dac_out, DAC_TRIGGER_SOFTWARE);
/* DAC trigger enable */
dac_trigger_enable(DAC0, dac_out);
/* DAC wave mode config */
dac_wave_mode_config(DAC0, dac_out, DAC_WAVE_DISABLE);
/* DAC output buffer config */
dac_output_buffer_enable(DAC0, dac_out);
/* DAC enable */
dac_enable(DAC0, dac_out);
dac_data_set(DAC0, dac_out, DAC_ALIGN_12B_R, 0);
dac_software_trigger_enable(DAC0, dac_out);
}
void gd_dac_dma_timer_init(uint8_t dac_out,uint32_t timer_periph,uint16_t *txbuff,uint16_t size)
{
rcu_periph_clock_enable(RCU_DAC);
gd_dac_out_gpio_config(dac_out);
gd_dac_dma_config(dac_out,txbuff,size);
/* initialize DAC */
/* DAC trigger config */
if(timer_periph == TIMER5){
dac_trigger_source_config(DAC0, dac_out, DAC_TRIGGER_T5_TRGO);}
else if(timer_periph == TIMER6){
dac_trigger_source_config(DAC0, dac_out, DAC_TRIGGER_T6_TRGO);
}
/* DAC trigger enable */
dac_trigger_enable(DAC0, dac_out);
/* DAC wave mode config */
dac_wave_mode_config(DAC0, dac_out, DAC_WAVE_DISABLE);
/* DAC enable */
dac_enable(DAC0, dac_out);
/* DAC DMA function enable */
dac_dma_enable(DAC0, dac_out);
}
/// @brief //输出LFSR噪声
/// @param dac_out DAC_OUT0 DAC_OUT1
/// @param value DAC偏置,最低电压
/// @param unmask_bits DAC振幅 最高电压 DAC_LFSR_BITS11_0 = 4095=3.3V
/// @param timer_periph 需要用定时器 TIMER5 TIMER6
void gd_dac_wave_lfsr_init(uint8_t dac_out ,uint16_t value ,uint32_t unmask_bits ,uint32_t timer_periph)
{
rcu_periph_clock_enable(RCU_DAC);
gd_dac_out_gpio_config(dac_out);
/* DAC trigger config */
if(timer_periph == TIMER5){
dac_trigger_source_config(DAC0, dac_out, DAC_TRIGGER_T5_TRGO);}
else if(timer_periph == TIMER6){
dac_trigger_source_config(DAC0, dac_out, DAC_TRIGGER_T6_TRGO);
}
/* DAC trigger enable */
dac_trigger_enable(DAC0, dac_out);
/* DAC wave mode config */
dac_wave_mode_config(DAC0, dac_out, DAC_WAVE_MODE_LFSR);
dac_lfsr_noise_config(DAC0, dac_out, unmask_bits);
/* DAC enable */
dac_enable(DAC0, dac_out);
dac_data_set(DAC0, dac_out, DAC_ALIGN_12B_R, value);
}
/// @brief //三角波
/// @param dac_out DAC_OUT0 DAC_OUT1
/// @param value DAC偏置,最低电压
/// @param amplitude DAC振幅 最高电压 DAC_TRIANGLE_AMPLITUDE_4095 = 4095=3.3V
void gd_dac_wave_triangle_init(uint8_t dac_out ,uint16_t value ,uint32_t amplitude,uint32_t timer_periph)
{
rcu_periph_clock_enable(RCU_DAC);
gd_dac_out_gpio_config(dac_out);
/* DAC trigger config */
if(timer_periph == TIMER5){
dac_trigger_source_config(DAC0, dac_out, DAC_TRIGGER_T5_TRGO);}
else if(timer_periph == TIMER6){
dac_trigger_source_config(DAC0, dac_out, DAC_TRIGGER_T6_TRGO);
}
/* DAC trigger enable */
dac_trigger_enable(DAC0, dac_out);
/* DAC wave mode config */
dac_wave_mode_config(DAC0, dac_out, DAC_WAVE_MODE_TRIANGLE);
dac_triangle_noise_config(DAC0, dac_out, amplitude);
/* DAC enable */
dac_enable(DAC0, dac_out);
dac_data_set(DAC0, dac_out, DAC_ALIGN_12B_R, value);
}
void gd_dac_set_convert_value(uint8_t dac_out,uint16_t data)
{
dac_data_set(DAC0, dac_out, DAC_ALIGN_12B_R, data);
dac_software_trigger_enable(DAC0, dac_out);
//dac_enable(DAC0, dac_out);
}
这外设配置的基本代码。
把dac输出波形的代码封装成API函数,方便其他文件调用
#ifndef __MCU_DAC_SIGNAL_H
#define __MCU_DAC_SIGNAL_H
#include "./am_type/am_type.h"
#include "./gd_dac/gd_dac.h"
//三角波-----------------------------------------------------------------------------------
//硬件三角波配置
void dac_triangle_signal_bsp_config(float freq);
//DAC+DMA+定时器三角波配置
void dac_triangle_signal_config( uint16_t min_mv, uint16_t max_mv,uint32_t freq);
//三角波输出信号
void dac_triangle_signal_start(void);
//三角波停止信号
void dac_triangle_signal_stop(void);
//---------------------------------------------------------------------------
//正玄波-------------------------
void dac_sine_signal_config( uint16_t min_mv, uint16_t max_mv,uint32_t freq);
void dac_sine_signal_start(void);
void dac_sine_signal_stop(void);
//----------------------------------
//pwm方波---------------------
void dac_pwm_signal_config( uint16_t min_mv, uint16_t max_mv,uint32_t freq);
void dac_pwm_signal_start(void);
void dac_pwm_signal_stop(void);
//-----------------------
//直流信号------------------
void dac_conver_value_signal_config(void);
void dac_set_out_conver_value_signal(uint16_t mv);
//--------------------------------------------------
#endif
#include "mcu_dac_signal.h"
#include "./gd_tim/gd_basice_tim.h"
#include <math.h> // 用于sin()函数
#define M_PI 3.141592
uint16_t dac_buff[1024];
uint16_t dac_pwm_buff[2];
#if 1
//三角波
/// @brief 硬件dac+定时器输出三角波 0-3.3
/// @param freq 0.1-200
void dac_triangle_signal_bsp_config(float freq)
{
uint16_t arr,psc;
double t0,t1;
t0 = 1000000/freq;
t1 = t0/(4095*2);
psc = 12;//0.1us
arr = (uint16_t)(t1*10);
printf("freq=%f,t0=%f,t1=%f,psc=%d,arr=%d\r\n",freq,t0,t1,psc,arr);
gd_basic_timer56_init(TIMER6,arr,psc,1);
gd_dac_wave_triangle_init(DAC_OUT0,0,DAC_TRIANGLE_AMPLITUDE_4095,TIMER6);
}
/// @brief 计算三角波的dac值
/// @param dacbuff 保存dac输出地址
/// @param size 点数,分辨率 64、128,256需要单数
/// @param min 峰谷
/// @param max 峰顶
static void dac_triangle_cal_dacbuff(uint16_t *dacbuff, uint16_t size,uint16_t min,uint16_t max)
{
uint16_t i=0;
uint16_t k;
double dac_scope;
if((dacbuff == NULL) ){return ;}
if(min < 0){
min=0;}
if(max > 4095){
max=4095 ;}
dac_scope = (double)(max - min);//范围
k = ((uint16_t)dac_scope)/(size/2);//等份
for(i=0;i < size;i++)
{
if(i <= size/2){
dacbuff[i] = i*k+min;//从低到高
}
else
{
dacbuff[i] =max-(i-size/2)*k ;//从高到低
}
}
dacbuff[0] = min;
dacbuff[size/2] = max;
}
/// @brief dac+dma+定时器输出三角波
/// @param min_mv 峰谷0-3300
/// @param max_mv 0-3300
/// @param freq 频率
void dac_triangle_signal_config( uint16_t min_mv, uint16_t max_mv,uint32_t freq)
{
uint16_t i,size ;
uint16_t min;
uint16_t max;
uint16_t arr,psc;
double t0,t1;
printf("dac triangle1 :min_mv=%d , max_mv=%d,freq=%d \r\n",min_mv,max_mv,freq);
min = (uint16_t)((min_mv*4095)/3300);
max = (uint16_t)((max_mv*4095)/3300);
if(freq > 100000){ freq = 100000;}
if(freq >= 50000){
size = 50;
}
else if(freq >= 10000){
size = 100;
}
else if(freq >= 1000){
size = 256;
}
else if(freq >= 1)
{
size = 512;
}
else
{
size = 1024;
}
printf("dac triangle2 :min=%d , max=%d,size=%d \r\n",min,max,size);
t0 = (1000000.f/freq); //us
t1 = (t0 / size);//一个点需要的时间
if(freq >= 1)
{
psc = 6;//0.05us
arr = (uint16_t)((t1*5)+0.5);
}
else
{
psc = 60;//0.5us
arr = (uint16_t)(t1*2+0.5);
}
printf("dac triangle3 :t0=%f , t1=%f,psc=%d ,arr=%d\r\n",t0,t1,psc,arr);
dac_triangle_cal_dacbuff(dac_buff,size,min,max);
// printf("dac triangle4 buff\r\n");
// for(i=0;i<size;i++){
// printf(" %d ",dac_buff[i]);
// }
// printf("\r\n");
gd_basic_timer56_init(TIMER6,arr,psc,1);
gd_dac_dma_timer_init(DAC_OUT0,TIMER6,dac_buff,size);
}
//开始信号
void dac_triangle_signal_start(void)
{
timer_enable(TIMER6);
}
//停止信号
void dac_triangle_signal_stop(void)
{
timer_disable(TIMER6);
printf("timer_disable\r\n");
}
#endif
#if 1
//正玄波
/// @brief 计算正弦波的dac值
/// @param dacbuff 保存dac输出地址
/// @param size 点数,建议使用较大的单数以获得更平滑的波形 63/ 127
/// @param min 波形最小值
/// @param max 波形最大值
void dac_sine_cal_dacbuff(uint16_t *dacbuff, uint16_t size, uint16_t min, uint16_t max)
{
uint16_t i;
double angle; // 角度(弧度)
double dac_scope; // 波形范围
double mid_value; // 中间值
// 参数合法性检查
if((dacbuff == NULL) ){
return;
}
if(min < 0){
min = 0;
}
if(max > 4095){
max = 4095;
}
// 计算波形范围和中间值
dac_scope = (double)(max - min);
mid_value = min + dac_scope / 2.0; // 正弦波的直流偏置
// 生成正弦波数据
for(i = 0; i < size; i++){
// 计算当前点对应的角度(0到2π)
angle = 2 * M_PI * i / (size - 1);
// 计算正弦值并映射到[min, max]范围
// sin(angle)范围是[-1, 1],转换为[min, max]范围
dacbuff[i] = (uint16_t)(mid_value + (dac_scope / 2.0) * sin(angle));
}
// 确保起点和终点值正确(对于完整周期的正弦波,起点和终点应接近min值)
//dacbuff[0] = min;
}
void dac_sine_signal_config( uint16_t min_mv, uint16_t max_mv,uint32_t freq)
{
uint16_t i,size ;
uint16_t min;
uint16_t max;
uint16_t arr,psc;
double t0,t1;
printf("dac sine1 :min_mv=%d , max_mv=%d,freq=%d \r\n",min_mv,max_mv,freq);
min = (uint16_t)((min_mv*4095)/3300);
max = (uint16_t)((max_mv*4095)/3300);
if(freq > 100000){ freq = 100000;}
if(freq >= 50000){
size = 50;
}
else if(freq >= 10000){
size = 100;
}
else if(freq >= 1000){
size = 256;
}
else if(freq >= 1)
{
size = 512;
}
else
{
size = 1024;
}
printf("dac sine2 :min=%d , max=%d,size=%d \r\n",min,max,size);
t0 = (1000000.f/freq); //us
t1 = (t0 / size);//一个点需要的时间
if(freq >= 1)
{
psc = 6;//0.05us
arr = (uint16_t)((t1*20)+0.5);
}
else
{
psc = 60;//0.5us
arr = (uint16_t)(t1*2+0.5);
}
printf("dac sine3 :t0=%f , t1=%f,psc=%d ,arr=%d\r\n",t0,t1,psc,arr);
dac_sine_cal_dacbuff(dac_buff,size,min,max);
// printf("dac sine14 buff\r\n");
// for(i=0;i<size;i++){
// printf(" %d ",dac_buff[i]);
// }
// printf("\r\n");
gd_basic_timer56_init(TIMER6,arr,psc,1);
gd_dac_dma_timer_init(DAC_OUT0,TIMER6,dac_buff,size);
}
//开始信号
void dac_sine_signal_start(void)
{
timer_enable(TIMER6);
}
//停止信号
void dac_sine_signal_stop(void)
{
timer_disable(TIMER6);
}
#endif
//dac-pwm方波------------------------------------
/// @brief dac-pwm方波
/// @param min_mv
/// @param max_mv
/// @param freq 1- 1M
void dac_pwm_signal_config( uint16_t min_mv, uint16_t max_mv,uint32_t freq)
{
uint16_t i,size;
uint16_t min,max;
uint16_t arr,psc;
double t0,t1;
if(min_mv < 0){
min_mv =0;
}
if(max_mv > 3300){
max_mv = 3300;
}
if(freq < 1){
freq = 1;
}
if(freq > 1000000){
freq = 1000000;
}
min = (uint16_t)((min_mv*4095)/3300);
max = (uint16_t)((max_mv*4095)/3300);
size = sizeof(dac_pwm_buff)/sizeof(dac_pwm_buff[0]);
for(i=0 ;i<size/2 ;i++ )
{
dac_pwm_buff[i]= min;
}
for(i=0 ;i< size/2;i++ )
{
dac_pwm_buff[i+size/2]= max;
}
t0 = (1000000.f/freq);
t1 = t0/size;
if(freq >= 100)
{
psc = 12;//0.1us
arr = (uint16_t)((t1*10)+0.5);
}
else if(freq >= 10)
{
psc = 12;//1us
arr = (uint16_t)(t1+0.5);
}else
{
psc = 1200;//10us
arr = (uint16_t)(t1+0.5);
}
gd_basic_timer56_init(TIMER6,arr,psc,1);
gd_dac_dma_timer_init(DAC_OUT0,TIMER6,dac_pwm_buff,size);
}
//开始信号
void dac_pwm_signal_start(void)
{
timer_enable(TIMER6);
}
//停止信号
void dac_pwm_signal_stop(void)
{
timer_disable(TIMER6);
}
//------------------------------------
//直流信号------------------
void dac_conver_value_signal_config(void)
{
gd_dac_convert_init(DAC_OUT0);
}
void dac_set_out_conver_value_signal(uint16_t mv)
{
uint16_t data;
data = (uint16_t)(mv*4095/3300);
gd_dac_set_convert_value(DAC_OUT0,data);
}
//-----------------------
驱动代码基本完成,直接调用函数输出波形就可以。
dac_sine_signal_config(0,3000,1000);
dac_pwm_signal_start();
这样子就是输出一个0-3V的1KHZ的正弦波了,dac_sine_signal_stop()这是停止输出,其实就是关闭定时器。
//--------------------------------------------------------------------------------------------------------------------------
因为MCU的DAC最大电压是3.3V,如果需要超过3.3V需用到运放,我目前放大了3倍
使用示波器看一下波形。
正弦波 频率:1K 振幅0-6V.
频率和电压有一点误差,需要软件补偿调整一下。
方波 频率:10K 振幅0-3V.
波形就很接近了。
//===================================================================
关于波形发送器基本就这样了
工程代码就不上传了,写的有点乱,特别OLED和按键交互哪里。
PCB我上传到嘉立创的硬件开源平台,这一版的AD9833输出最终信号输出端,在高频5M以上的时候会有问题,因该是我后级运放有点问题,还有可能是高频对MCP41010干扰太大了。AD9833芯片出来的信号是正常的。

为武汉地区的开发者提供学习、交流和合作的平台。社区聚集了众多技术爱好者和专业人士,涵盖了多个领域,包括人工智能、大数据、云计算、区块链等。社区定期举办技术分享、培训和活动,为开发者提供更多的学习和交流机会。
更多推荐
所有评论(0)