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, 可以用運算放大電路放大。

硬件:

  1. MCU :GD32F303CBT6
  2. DAC輸出通道0 :AD9833+MCP41010+SPI
  3. DAC輸出通道1:MCU外設DAC
  4. OLED
  5. 按鍵
  6. 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"//軟件模擬SPItypedef 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 PA5gd_gpio_config(GPIOA,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_5);//SPI0 MOSI PA7gd_gpio_config(GPIOA,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_7);//SPI0 MISO PA6gd_gpio_config(GPIOA,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,GPIO_PIN_6);}else if(spi_periph == SPI1){//SPI1 SCL PB13gd_gpio_config(GPIOB,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_13);//SPI1 MOSI PB15gd_gpio_config(GPIOB,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_15);//SPI1 MISO PB14gd_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 PB3gd_gpio_config(GPIOB,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_3);//SPI2 MOSI PB5gd_gpio_config(GPIOB,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_5);//SPI2 MISO PB4gd_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 PB3gd_gpio_config(GPIOB,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_3);//SPI0 MOSI PB5gd_gpio_config(GPIOB,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_5);//SPI0 MISO PB4gd_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 PC10gd_gpio_config(GPIOC,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_10);//SPI2 MOSI PC12gd_gpio_config(GPIOC,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_12);//SPI2 MISO PC11gd_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;}// 準備要發送的數據,如果發送緩沖區為空則發送0xFFtx_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/2DAC_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.3vvoid 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);elseGD_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最大支持25MHzfrequence_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_LSBfrequence_LSB=frequence_LSB&0x3fff;//去除最高兩位,16位數換去掉高位后變成了14位frequence_MSB=frequence_hex>>14; //frequence_hex高16位送給frequence_HSBfrequence_MSB=frequence_MSB&0x3fff;//去除最高兩位,16位數換去掉高位后變成了14位Phs_data=Phase|0xC000;	//相位值ad9833_write_data(0x0100); //復位AD9833,即RESET位為1ad9833_write_data(0x2100); //選擇數據一次寫入,B28位和RESET位為1if(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 1void 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);}#endifvoid 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);elseGD_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.2Vad9833_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.141592uint16_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.1usarr = (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); //ust1 = (t0 / size);//一個點需要的時間if(freq >= 1){psc = 6;//0.05usarr = (uint16_t)((t1*5)+0.5);}else{psc = 60;//0.5usarr = (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); //ust1 = (t0 / size);//一個點需要的時間if(freq >= 1){psc = 6;//0.05usarr = (uint16_t)((t1*20)+0.5);}else{psc = 60;//0.5usarr = (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.1usarr = (uint16_t)((t1*10)+0.5);}else if(freq >= 10){psc = 12;//1usarr = (uint16_t)(t1+0.5);}else{psc = 1200;//10usarr = (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芯片出來的信號是正常的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/94160.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/94160.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/94160.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

VS2022的MFC中關聯使用控制臺并用printf輸出調試信息

前言 MFC一般在調試的時候&#xff0c;可以在IDE中方便的看到調試的信息。但是&#xff0c;有時候運行的時候也要看調試的信息怎么辦&#xff1f;最好如同在Console&#xff08;控制臺&#xff09;程序中輸出一般的方便&#xff0c;可以么&#xff1f;可以的。 一、設置 1.1、加…

ZKmall模塊商城的推薦數據體系:從多維度采集到高效存儲的實踐

在電商領域&#xff0c;個性化推薦已成為提升用戶體驗與轉化效率的核心手段。ZKmall 模塊商城基于用戶行為、商品屬性與交易數據&#xff0c;構建了一套完整的推薦算法體系&#xff0c;而數據采集的全面性與存儲的高效性是該體系的基礎。本文將聚焦推薦算法的 “數據輸入端”&a…

Qt + windows+exe+msvc打包教程

目錄 1. Qt + windows+exe+msvc打包教程1 1.1. Enigma Virtual Box下載?1 1.2. Enigma Virtual Box安裝2 1.3. Qt 打包成獨立exe教程6 1.3.1. Qt項目創建6 1.3.2. Qt項目編譯13 1.3.3. Qt 項目打包 windeployqt命令14 1.3.4. Qt 項目打包 Enigma Virtual Box工具18 Q…

大語言模型應用開發——利用OpenAI函數與LangChain結合從文本構建知識圖譜搭建RAG應用全流程

概述 從文本等非結構化數據中提取結構化信息并非新鮮事物&#xff0c;但大語言模型&#xff08;LLMs&#xff09;為該領域帶來了重大變革。以往需要機器學習專家團隊策劃數據集并訓練自定義模型&#xff0c;如今只需訪問LLM即可實現&#xff0c;顯著降低了技術門檻&#xff0c…

Vue3+Spring Boot技術棧,前端提交混合表單數據(普通字段+文件字段),上傳文件,后端插入數據,將文件保存到數據庫

一、技術棧1、前端 Vue3 Element Plus TypeSprict2、后端 Spring Boot 3.2.12 Mybatis Plus3、模型特點3.1、表格展示列表數據3.2、行點擊&#xff0c;彈出對話框3.3、前端使用 FormData 提交混合表單數據&#xff0c;包含普通字段和文件字段3.4、文件對應數據庫結構類型為 …

【Qt開發】Qt的背景介紹(四)

目錄 1 -> Qt Hello World 程序 1.1 -> 使用“按鈕”實現 1.1.1 -> 純代碼方式實現 1.1.2 -> 可視化操作實現 1.2 -> 使用“標簽”實現 1.2.1 -> 純代碼方式實現 1.2.2 -> 可視化操作實現 2 -> 項目文件解析 2.1 -> .pro文件解析 2.2 -&g…

Linux驅動開發筆記(六)——pinctrl GPIO

開發板&#xff1a;imx6ull mini 虛擬機&#xff1a;VMware17 ubuntu&#xff1a;ubuntu20.04 視頻&#xff1a;第8.1講 pinctrl和gpio子系統試驗-pincrl子系統詳解_嗶哩嗶哩_bilibili 文檔&#xff1a;《【正點原子】I.MX6U嵌入式Linux驅動開發指南.pdf》四十五章 這一章…

SpringBoot 快速上手:從環境搭建到 HelloWorld 實戰

在 Java 開發領域&#xff0c;Spring 框架占據著舉足輕重的地位&#xff0c;但它復雜的配置曾讓不少開發者望而卻步。SpringBoot 的出現&#xff0c;如同為 Spring 框架裝上了 “加速器”&#xff0c;以 “約定大于配置” 的理念簡化了開發流程。本文將從環境準備、Maven 配置入…

圖、最小生成樹與最短路徑

目錄 并查集 并查集實現 圖 概念 圖的存儲結構 鄰接矩陣 鄰接表 無向圖 有向圖 圖的遍歷 廣度優先遍歷 深度優先遍歷 最小生成樹 Kruskal算法&#xff08;克魯斯卡爾算法&#xff09; Prim算法&#xff08;普利姆算法&#xff09; 最短路徑 單源最短路徑--Dij…

互聯網電商新生態:開源AI智能名片、鏈動2+1模式與S2B2C商城小程序的融合賦能

摘要&#xff1a;本文聚焦互聯網電商領域&#xff0c;探討在當下直播電商蓬勃發展的背景下&#xff0c;開源AI智能名片、鏈動21模式以及S2B2C商城小程序如何相互融合&#xff0c;為創業者、企業和淘寶主播IP等電商參與者帶來新的發展機遇。通過分析各要素的特點與優勢&#xff…

企業車輛|基于SprinBoot+vue的企業車輛管理系統(源碼+數據庫+文檔)

企業車輛管理系統 基于SprinBootvue的企業車輛管理系統 一、前言 二、系統設計 三、系統功能設計 系統功能實現 后臺模塊實現 管理員模塊實現 駕駛員模塊實現 四、數據庫設計 五、核心代碼 六、論文參考 七、最新計算機畢設選題推薦 八、源碼獲取&#xff1a; 博…

自學嵌入式第二十五天:數據結構-隊列、樹

一、隊列隊列是只允許一段進行插入&#xff0c;另一端進行刪除操作的線性表&#xff1b;允許插入的一端叫隊尾&#xff0c;允許刪除的一端叫對頭&#xff1b;先進先出&#xff1b;用于解決速度不匹配&#xff08;例如一快一慢&#xff09;&#xff0c;做緩沖用&#xff1b;二、…

MySQL索引原理與優化全解析

1、MySQL索引是什么&#xff1f; 在關系數據庫中&#xff0c;索引是一種單獨的、物理的對數據庫表中一列或多列的值進行排序的一種存儲結構&#xff0c;它是某個表中一列或若干列值的集合和相應的指向表中物理標志這些值的數據頁的邏輯指針清單。索引的作用相當于圖書的目錄&a…

模型對話狀態管理方法詳解

模型對話狀態管理方法詳解 目錄 簡介手動管理對話狀態構建對話歷史追加響應內容 API 支持的自動化對話狀態管理使用 previous_response_id 鏈接話輪 Token 及上下文窗口管理上下文窗口定義與限制Token 計數與工具 安全與合規注意事項結語1. 簡介 在多輪對話場景中&#xff0c;合…

GPT-5 上線風波深度復盤:從口碑兩極到策略調整,OpenAI 的變與不變

摘要&#xff1a; 近日&#xff0c;備受矚目的 GPT-5 正式上線&#xff0c;卻意外地在社區引發了兩極化爭議。面對技術故障與用戶質疑&#xff0c;OpenAI 迅速推出一系列補救措施。本文將深度復盤此次發布風波&#xff0c;解析其背后的技術挑戰與應對策略&#xff0c;并探討這一…

【Android】使用FragmentManager動態添加片段

三三要成為安卓糕手 上一篇文章&#xff0c;我們是在xml中靜態添加fragment&#xff0c;但是一些修改或者其他事情是做不了的&#xff1b; 本章我們達成在java代碼中靈活添加、刪除、替換fragment操作 一&#xff1a;核心代碼展示 簡單做一個這種頁面public class FragmentActi…

MiniOB環境部署開發(使用開源學堂)

整體思路&#xff1a; 1.使用開源學堂在線編程環境開發MiniOB編譯環境 2.使用vscode進行代碼調試和開發以及上傳到倉庫 MiniOB源碼&#xff1a;https://github.com/oceanbase/miniob MiniOB文檔&#xff1a;MiniOB 介紹 - MiniOB 數據庫大賽官網&#xff1a;OceanBase 社區…

09_常用內置模塊進階

第9課&#xff1a;常用內置模塊進階 課程目標 深入學習Python常用內置模塊掌握collections、itertools、functools等模塊學習json、csv、pickle等數據處理模塊 1. collections模塊 1.1 Counter類 from collections import Counter# 統計元素出現次數 text "hello world p…

? Ranger 基礎命令與功能詳解

&#x1f4cc; 1. Ranger簡介 Ranger&#xff08;游俠&#xff09;是一款 Linux 專用的 指令式文件管理器&#xff0c;其操作風格類似 Vim&#xff0c;通過輸入指令即可完成目錄跳轉、文件編輯、移動、復制等操作。 相比于 mc&#xff08;Midnight Commander&#xff09;&…

CUDA安裝教程(包括cuDNN的教程)一個博客帶你了解所有問題

前言 windows10 版本安裝 CUDA &#xff0c;首先需要下載兩個安裝包 CUDA toolkit&#xff08;toolkit就是指工具包&#xff09;cuDNN 注&#xff1a;cuDNN 是用于配置深度學習使用 官方教程 CUDA&#xff1a;Installation Guide Windows :: CUDA Toolkit Documentation …