Last active
May 24, 2023 14:52
-
-
Save TrebledJ/5c45ba3366918352a3d56625a636bafa to your computer and use it in GitHub Desktop.
STM32F405 example for 440Hz sine wave generation.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// main.c | |
#include "stm32f4xx_hal.h" | |
#include <math.h> | |
#include <stdint.h> | |
DAC_HandleTypeDef hdac; | |
DMA_HandleTypeDef hdma_dac1; | |
DMA_HandleTypeDef hdma_dac2; | |
TIM_HandleTypeDef htim8; | |
void SystemClock_Config(void); | |
static void MX_DMA_Init(void); | |
static void MX_DAC_Init(void); | |
static void MX_TIM8_Init(void); | |
int main(void) | |
{ | |
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */ | |
HAL_Init(); | |
/* Configure the system clock */ | |
SystemClock_Config(); | |
/* Initialize all configured peripherals */ | |
MX_DMA_Init(); | |
MX_DAC_Init(); | |
MX_TIM8_Init(); | |
#define SAMPLE_RATE 42000 | |
#define BUFFER_SIZE 1024 | |
#define FREQUENCY 440 | |
uint16_t buffers[2][BUFFER_SIZE]; | |
uint8_t curr = 0; | |
uint32_t t = 0; | |
// Start the timer. | |
HAL_TIM_Base_Start(&htim8); | |
// Optimisation: precompute constant. | |
float two_pi_f_over_sr = 2 * M_PI * FREQUENCY / SAMPLE_RATE; | |
while (1) { | |
// Prep the buffer. | |
uint16_t* buffer = buffers[curr]; | |
for (int i = 0; i < BUFFER_SIZE; i++, t++) { | |
buffer[i] = 2047 * sin(two_pi_f_over_sr * t) + 2047; | |
} | |
// Wait for DAC to be ready. | |
while (HAL_DAC_GetState(&hdac) != HAL_DAC_STATE_READY) | |
; | |
// Start the DMA. | |
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)buffer, BUFFER_SIZE, DAC_ALIGN_12B_R); | |
curr = !curr; | |
} | |
} | |
// The rest is code generated by STM32 CubeMX. | |
// Included for completeness. | |
// -------------------------------------------- | |
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 RCC Oscillators according to the specified parameters | |
* in the RCC_OscInitTypeDef structure. | |
*/ | |
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 = 7; | |
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { | |
Error_Handler(); | |
} | |
/** Initializes the CPU, AHB and APB buses 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(); | |
} | |
} | |
static void MX_DAC_Init(void) | |
{ | |
DAC_ChannelConfTypeDef sConfig = {0}; | |
/** DAC Initialization | |
*/ | |
hdac.Instance = DAC; | |
if (HAL_DAC_Init(&hdac) != HAL_OK) { | |
Error_Handler(); | |
} | |
/** DAC channel OUT1 config | |
*/ | |
sConfig.DAC_Trigger = DAC_TRIGGER_T8_TRGO; | |
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; | |
if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK) { | |
Error_Handler(); | |
} | |
/** DAC channel OUT2 config | |
*/ | |
if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_2) != HAL_OK) { | |
Error_Handler(); | |
} | |
} | |
static void MX_TIM8_Init(void) | |
{ | |
TIM_ClockConfigTypeDef sClockSourceConfig = {0}; | |
TIM_MasterConfigTypeDef sMasterConfig = {0}; | |
TIM_OC_InitTypeDef sConfigOC = {0}; | |
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0}; | |
htim8.Instance = TIM8; | |
htim8.Init.Prescaler = 0; | |
htim8.Init.CounterMode = TIM_COUNTERMODE_UP; | |
htim8.Init.Period = 4000 - 1; | |
htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; | |
htim8.Init.RepetitionCounter = 0; | |
htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; | |
if (HAL_TIM_Base_Init(&htim8) != HAL_OK) { | |
Error_Handler(); | |
} | |
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; | |
if (HAL_TIM_ConfigClockSource(&htim8, &sClockSourceConfig) != HAL_OK) { | |
Error_Handler(); | |
} | |
if (HAL_TIM_PWM_Init(&htim8) != HAL_OK) { | |
Error_Handler(); | |
} | |
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; | |
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; | |
if (HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig) != HAL_OK) { | |
Error_Handler(); | |
} | |
sConfigOC.OCMode = TIM_OCMODE_PWM1; | |
sConfigOC.Pulse = 0; | |
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; | |
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; | |
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; | |
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; | |
if (HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_4) != HAL_OK) { | |
Error_Handler(); | |
} | |
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE; | |
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE; | |
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF; | |
sBreakDeadTimeConfig.DeadTime = 0; | |
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE; | |
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH; | |
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE; | |
if (HAL_TIMEx_ConfigBreakDeadTime(&htim8, &sBreakDeadTimeConfig) != HAL_OK) { | |
Error_Handler(); | |
} | |
HAL_TIM_MspPostInit(&htim8); | |
} | |
static void MX_DMA_Init(void) | |
{ | |
/* DMA controller clock enable */ | |
__HAL_RCC_DMA1_CLK_ENABLE(); | |
/* DMA interrupt init */ | |
/* DMA1_Stream4_IRQn interrupt configuration */ | |
HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0); | |
HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn); | |
/* DMA1_Stream5_IRQn interrupt configuration */ | |
HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 0, 0); | |
HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn); | |
/* DMA1_Stream6_IRQn interrupt configuration */ | |
HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 0, 0); | |
HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn); | |
} | |
void Error_Handler(void) | |
{ | |
__disable_irq(); | |
while (1) {} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// stm32f4xx_it.c | |
// If initialised correctly, you should have the usual interrupts, along with two DMA DAC | |
// interrupts (one for each DAC channel). These are auto-generated by CubeMX. | |
#include "main.h" | |
#include "stm32f4xx_it.h" | |
// -- snip -- | |
extern DMA_HandleTypeDef hdma_dac1; | |
extern DMA_HandleTypeDef hdma_dac2; | |
// -- snip -- | |
void SysTick_Handler(void) | |
{ | |
HAL_IncTick(); | |
} | |
void DMA1_Stream5_IRQHandler(void) | |
{ | |
HAL_DMA_IRQHandler(&hdma_dac1); | |
} | |
void DMA1_Stream6_IRQHandler(void) | |
{ | |
HAL_DMA_IRQHandler(&hdma_dac2); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment