Sawtooth and sine waveform from onchip 12-bit DAC running at 2 MS/s on this STM32G431CBU6 board.
The sawtooth signal is increment on each next sample.
The sine signal is generated at realtime 
Any chance of a link/zip to the sourcecode for this demo
/Bingo
here is source code for this test demo which generate sine wave with hardcoded frequency on PA4 DAC output, also it put HSE clock to PA8 and use LED blink in background.
A short description: PLL is configured to be clocked from external HSE 8 MHz clock and produce 170 MHz clock, which is used for SYSCLK and as a source for TIM2. TIM2 is configured to be triggered at 2 MHz rate. TIM2 interrupt handler contains code to calculate sine wave and update DAC with a new value.
PLL frequency is the following:
Fout = ((Finput / PLLM) * PLLN) / PLLR
Since Finput= 8 MHz, we use PLLM=2, PLLN=85 and PLLR=2, so the PLL output frequency will be:
Fout = ((8 MHz / 2) * 85) / 2 = 170 MHz
You can change sine frequency on DAC output in init_sine argument. Note that you're needs to update sample rate in init_sine arguments if you change TIM2 divider.
Header files consume 13 MB of space even without HAL, but I think it's better to include it. All required files are included in archive.
for GCC compiller just enter to GCC folder and type make.
for EWARM enter to EWARM folder and use included workspace file.
here is main.c:
#include <math.h>
#include <stm32g431.h>
// test code for STM32G431CBU6 module
// [url]https://www.eevblog.com/forum/microcontrollers/blackpill-stm32g431cbu6-170-mhz-32k-ram-128k-rom-12-bit-adc-and-dac/[/url]
//
// PC6 = LED
// PC13 = KEY
// PA4 = DAC out
// PA8 = HSE out
void init_gpio(void)
{
// Enable GPIOC clock
SET_BIT(RCC->AHB2ENR, RCC_AHB2ENR_GPIOCEN);
// PC6 = OUT (LED)
MODIFY_REG(GPIOC->MODER,
GPIO_MODER_MODE6,
1 << GPIO_MODER_MODE6_Pos);
// PC6 speed
MODIFY_REG(GPIOC->OSPEEDR,
GPIO_OSPEEDR_OSPEED6_Msk,
GPIO_SPEED_FREQ_VERY_HIGH << GPIO_OSPEEDR_OSPEED6_Pos);
// PC13 = IN (KEY)
MODIFY_REG(GPIOC->MODER,
GPIO_MODER_MODE13,
0 << GPIO_MODER_MODE13_Pos);
// Enable GPIOA clock
SET_BIT(RCC->AHB2ENR, RCC_AHB2ENR_GPIOAEN);
// PA8 = ALTFN MCO
MODIFY_REG(GPIOA->MODER,
GPIO_MODER_MODE8,
2 << GPIO_MODER_MODE8_Pos);
// PA8 alternate function MCO
MODIFY_REG(GPIOA->AFR[8 >> 3U],
GPIO_AFRH_AFSEL8_Msk, // 0xFU << ((8 & 0x07U) * 4U),
GPIO_AF0_MCO << GPIO_AFRH_AFSEL8_Pos); // GPIO_AF0_MCO << ((8 & 0x07U) * 4U));
// PA8 speed
MODIFY_REG(GPIOA->OSPEEDR,
GPIO_OSPEEDR_OSPEED8_Msk,
GPIO_SPEED_FREQ_VERY_HIGH << GPIO_OSPEEDR_OSPEED8_Pos);
// PA8 pull mode
MODIFY_REG(GPIOA->PUPDR,
GPIO_PUPDR_PUPD8_Msk,
GPIO_NOPULL << GPIO_PUPDR_PUPD8_Pos);
// Configure MCO = HSE / 1
MODIFY_REG(RCC->CFGR,
RCC_CFGR_MCOSEL | RCC_CFGR_MCOPRE,
RCC_MCO1SOURCE_HSE | RCC_MCODIV_1);
}
void init_clock(void)
{
// Select SYSCLK source = HSI
MODIFY_REG(RCC->CFGR, RCC_CFGR_SW, RCC_CFGR_SW_HSI);
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI);
// Stop PLL
CLEAR_BIT(RCC->CR, RCC_CR_PLLON);
while ((RCC->CR & RCC_CR_PLLRDY) != 0);
// Enable high voltage for power regulator to 1.28 V
CLEAR_BIT(PWR->CR5, PWR_CR5_R1MODE);
// Configure FLASH latency = 4
FLASH->ACR = FLASH_ACR_DBG_SWEN | FLASH_ACR_DCEN | FLASH_ACR_ICEN | FLASH_ACR_LATENCY_4WS ;
while ((FLASH->ACR & FLASH_ACR_LATENCY_Msk) != FLASH_ACR_LATENCY_4WS);
// Power on HSE crystal
SET_BIT(RCC->CR, RCC_CR_HSEON);
while ((RCC->CR & RCC_CR_HSERDY) == 0);
// Configure PLL used for SYSCLK
// PLLM=2 (value 1), PLLN=85 (value 85), PLLR=2 (value 0)
MODIFY_REG(RCC->PLLCFGR,
RCC_PLLCFGR_PLLSRC | RCC_PLLCFGR_PLLM | RCC_PLLCFGR_PLLN | RCC_PLLCFGR_PLLR,
RCC_PLLCFGR_PLLSRC_HSE | (1 <<RCC_PLLCFGR_PLLM_Pos) | (85 << RCC_PLLCFGR_PLLN_Pos) | (0 << RCC_PLLCFGR_PLLR_Pos));
// Start PLL
SET_BIT(RCC->CR, RCC_CR_PLLON);
while ((RCC->CR & RCC_CR_PLLRDY) == 0);
// Enable PLL output for SYSCLK
SET_BIT(RCC->PLLCFGR, RCC_PLLCFGR_PLLREN);
// Select SYSCLK source = PLL
MODIFY_REG(RCC->CFGR, RCC_CFGR_SW, RCC_CFGR_SW_PLL);
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
void init_TIM2(void)
{
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_TIM2EN); // Enable TIM2 clock
TIM2->ARR = 85 - 1; // Auto-reload register f = 170 000 000/(TIM2_ARR+1), 2 MHz -> (85 - 1)
SET_BIT(NVIC->ISER[0], (1 << 28)); // Enable NVIC interrupt #28 (TIM2)
SET_BIT(TIM2->DIER, TIM_DIER_UIE); // Enable timer overflow interrupt
SET_BIT(TIM2->CR1, TIM_CR1_CEN); // Enable timer
}
// DAC1 on PA4
void init_DAC1(void)
{
SET_BIT(RCC->AHB2ENR, RCC_AHB2ENR_DAC1EN); // Enable DAC1 clock
SET_BIT(DAC1->CR, DAC_CR_EN1); // Enable DAC1 output
DAC1->DHR12R1 = 0; // Write DAC1 = 0
}
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
float R;
float V;
float X;
float S;
void init_sine(float freq, uint32_t sample_rate)
{
// init sine oscillator
R = pow(2.0 * sin(freq * M_PI / sample_rate), 2.0); // = 4.0 * (freq * M_PI / sample_rate) * (freq * M_PI / sample_rate);
V = 0;
X = 2000; // initial value (amplitude)
S = 2048; // offset
}
void main()
{
init_sine(10000.0, 2000000);
init_gpio();
init_clock();
init_DAC1();
init_TIM2();
for (;;)
{
for (uint32_t i=1000000; i > 0; i--); // delay
GPIOC->ODR ^= GPIO_ODR_OD6; // change LED state
}
}
void TIM2_IRQHandler(void)
{
CLEAR_BIT(TIM2->SR, TIM_SR_UIF); // Reset TIM2 overflow flag
// update sine oscillator
V -= X * R;
X += V;
DAC1->DHR12R1 = (uint32_t)(X + S);
}
//----------------------------------------------------
void NMI_Handler(void)
{
__asm("BKPT 0");
}
void HardFault_Handler(void)
{
__asm("BKPT 0");
}
void MemManage_Handler(void)
{
__asm("BKPT 0");
}
void BusFault_Handler(void)
{
__asm("BKPT 0");
}
void UsageFault_Handler(void)
{
__asm("BKPT 0");
}
PS: stm32g431.h is my file, it includes standard stm32g4xx.h and stm32g431xx.h and also add some constants taken from HAL headers. I don't include HAL, because it eats > 70 MB of space.
PPS: in order to build it on linux you're needs to install:
sudo pacman -S arm-none-eabi-gcc arm-none-eabi-newlib