STM32F103C8T6 是意法半导体(STMicroelectronics)推出的一款基于 ARM Cortex-M3 内核的 32 位微控制器,属于 STM32F1 系列("增强型" 产品线)。它以高性能、低成本和丰富的外设资源著称,广泛应用于工业控制、消费电子、物联网等领域。
内核与性能
存储器
外设资源
工作条件
以 STM32CubeIDE 为例:
下载并安装 STM32CubeIDE
创建新项目
配置外设
生成代码
编写应用代码
直接操作外设寄存器,代码效率高但开发难度大。
示例:使用寄存器点亮 LED
// 使能GPIOA时钟
*(unsigned int*)0x40021018 |= (1 << 2);
// 配置PA8为推挽输出,最大速度50MHz
*(unsigned int*)0x40010800 &= ~(0xF << 0); // 清除PA8配置
*(unsigned int*)0x40010800 |= (0x3 << 0); // 配置为输出模式
*(unsigned int*)0x40010800 |= (0x1 << 2); // 配置为50MHz速度
// 点亮LED (PA8输出高电平)
*(unsigned int*)0x4001080C |= (1 << 8);
使用 ST 提供的标准外设库或 HAL 库,简化开发过程。
示例:使用标准外设库点亮 LED
#include "stm32f10x.h"
int main(void) {
// 使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置GPIOA.8为推挽输出
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 点亮LED
GPIO_SetBits(GPIOA, GPIO_Pin_8);
while (1) {
// 主循环
}
}
示例:使用 HAL 库点亮 LED
#include "main.h"
GPIO_InitTypeDef GPIO_InitStruct = {0};
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
while (1) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
HAL_Delay(500);
}
}
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** 初始化RCC振荡器
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
/** 初始化RCC时钟
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) {
Error_Handler();
}
}
static void MX_GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
GPIO (通用输入输出) 是 STM32 最基本的外设,可以配置为多种模式:
示例:配置 GPIO 为输出模式
#include "stm32f10x.h"
void GPIO_Configuration(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA和GPIOC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);
// 配置PA8为推挽输出,用于控制LED
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置PC13为上拉输入,用于读取按键
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
示例代码:
#include "stm32f10x.h"
void Delay(__IO uint32_t nCount);
int main(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置PA8为推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
while (1) {
// 点亮LED
GPIO_SetBits(GPIOA, GPIO_Pin_8);
Delay(0xFFFFF);
// 熄灭LED
GPIO_ResetBits(GPIOA, GPIO_Pin_8);
Delay(0xFFFFF);
}
}
// 简单延时函数
void Delay(__IO uint32_t nCount) {
for (; nCount != 0; nCount--);
}
示例代码:
#include "stm32f10x.h"
void Delay(__IO uint32_t nCount);
int main(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA和GPIOC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);
// 配置PA8为推挽输出 (LED)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置PC13为上拉输入 (按键)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOC, &GPIO_InitStructure);
while (1) {
// 检测按键是否按下 (PC13为低电平)
if (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13) == 0) {
Delay(0x20000); // 消抖
if (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13) == 0) {
// 按键确实按下,切换LED状态
GPIO_WriteBit(GPIOA, GPIO_Pin_8,
(BitAction)(1 - GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_8)));
// 等待按键释放
while (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13) == 0);
}
}
}
}
void Delay(__IO uint32_t nCount) {
for (; nCount != 0; nCount--);
}
STM32F103C8T6 包含多个定时器:
示例:配置 TIM3 产生 1ms 定时中断
#include "stm32f10x.h"
void TIM3_Configuration(void) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 使能TIM3时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// TIM3配置
TIM_TimeBaseStructure.TIM_Period = 7199; // 自动重载值
TIM_TimeBaseStructure.TIM_Prescaler = 9; // 预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 使能TIM3中断
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
// 配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 使能TIM3
TIM_Cmd(TIM3, ENABLE);
}
// TIM3中断服务函数
void TIM3_IRQHandler(void) {
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
// 在这里添加定时处理代码
}
}
PWM (脉冲宽度调制) 常用于电机控制、LED 亮度调节等场景。
示例:配置 TIM3 CH1 产生 PWM 信号控制 LED 亮度
#include "stm32f10x.h"
void TIM3_PWM_Configuration(void) {
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 使能GPIOA和TIM3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// 配置PA6为复用推挽输出 (TIM3 CH1)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// TIM3基本配置
TIM_TimeBaseStructure.TIM_Period = 999; // PWM周期
TIM_TimeBaseStructure.TIM_Prescaler = 71; // 预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// TIM3 PWM模式配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 输出使能
TIM_OCInitStructure.TIM_Pulse = 500; // 初始占空比
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 输出极性高
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
// 使能TIM3
TIM_Cmd(TIM3, ENABLE);
}
int main(void) {
uint16_t pulse = 0;
uint8_t direction = 1;
TIM3_PWM_Configuration();
while (1) {
// 渐变LED亮度
if (direction) {
pulse++;
if (pulse >= 999) direction = 0;
} else {
pulse--;
if (pulse <= 0) direction = 1;
}
// 更新PWM脉冲宽度
TIM_SetCompare1(TIM3, pulse);
// 延时
for (int i = 0; i < 10000; i++);
}
}
UART (通用异步收发传输器) 是一种串行通信协议,常用于设备间的数据传输。STM32F103C8T6 包含 5 个 USART 接口,支持多种通信模式。
示例:配置 USART1 实现基本的串口通信
#include "stm32f10x.h"
#include
void USART1_Configuration(void) {
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 使能GPIOA和USART1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
// 配置PA9为复用推挽输出 (USART1 TX)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置PA10为浮空输入 (USART1 RX)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// USART1配置
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
// 使能USART1接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
// 配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 使能USART1
USART_Cmd(USART1, ENABLE);
}
// 重定向printf函数到USART1
int fputc(int ch, FILE *f) {
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, (uint8_t)ch);
return ch;
}
// USART1中断服务函数
void USART1_IRQHandler(void) {
uint8_t ch;
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
// 读取接收到的数据
ch = USART_ReceiveData(USART1);
// 回显数据
USART_SendData(USART1, ch);
// 清除中断标志
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
int main(void) {
USART1_Configuration();
printf("Hello, STM32F103!\r\n");
while (1) {
// 主循环可以处理其他任务
}
}
下面是一个通过串口命令控制 LED 的示例:
#include "stm32f10x.h"
#include
void GPIO_Configuration(void);
void USART1_Configuration(void);
void Delay(__IO uint32_t nCount);
// 全局变量
uint8_t led_status = 0; // 0: LED熄灭, 1: LED点亮
int main(void) {
GPIO_Configuration();
USART1_Configuration();
printf("LED Control Demo\r\n");
printf("Type 'on' to turn LED on, 'off' to turn LED off\r\n");
while (1) {
// 主循环可以处理其他任务
}
}
// USART1中断服务函数
void USART1_IRQHandler(void) {
static uint8_t rx_buffer[10] = {0};
static uint8_t rx_index = 0;
uint8_t ch;
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
// 读取接收到的数据
ch = USART_ReceiveData(USART1);
// 回显数据
USART_SendData(USART1, ch);
// 处理命令
if (ch == '\r' || ch == '\n' || rx_index >= 9) {
rx_buffer[rx_index] = '\0';
rx_index = 0;
// 处理命令
if (strcmp((char*)rx_buffer, "on") == 0) {
GPIO_SetBits(GPIOA, GPIO_Pin_8);
led_status = 1;
printf("LED turned ON\r\n");
} else if (strcmp((char*)rx_buffer, "off") == 0) {
GPIO_ResetBits(GPIOA, GPIO_Pin_8);
led_status = 0;
printf("LED turned OFF\r\n");
} else {
printf("Unknown command: %s\r\n", rx_buffer);
printf("Valid commands: on, off\r\n");
}
} else {
rx_buffer[rx_index++] = ch;
}
// 清除中断标志
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
void GPIO_Configuration(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置PA8为推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void USART1_Configuration(void) {
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 使能GPIOA和USART1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
// 配置PA9为复用推挽输出 (USART1 TX)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置PA10为浮空输入 (USART1 RX)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// USART1配置
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
// 使能USART1接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
// 配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 使能USART1
USART_Cmd(USART1, ENABLE);
}
void Delay(__IO uint32_t nCount) {
for (; nCount != 0; nCount--);
}
// 重定向printf函数到USART1
int fputc(int ch, FILE *f) {
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, (uint8_t)ch);
return ch;
}
STM32F103C8T6 内置 3 个 12 位 ADC(ADC1、ADC2 和 ADC3),支持多达 16 个外部通道,可测量 0-3.6V 范围内的模拟信号。ADC 具有单次、连续、扫描或间断模式,可通过定时器或外部触发启动转换。
以下是配置 ADC1 通道 0(PA0)进行单次转换的代码:
#include "stm32f10x.h"
void ADC1_Configuration(void) {
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
// 使能GPIOA和ADC1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
// 配置PA0为模拟输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// ADC1配置
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // 独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // 单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 软件触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; // 转换通道数
ADC_Init(ADC1, &ADC_InitStructure);
// 配置ADC1通道0的采样时间
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_28Cycles5);
// 使能ADC1
ADC_Cmd(ADC1, ENABLE);
// 校准ADC
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));
}
uint16_t Get_ADC1_Value(void) {
// 启动软件转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
// 等待转换完成
while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
// 读取ADC值并返回
return ADC_GetConversionValue(ADC1);
}
配置 ADC1 为连续转换模式,配合 DMA 实现高效数据采集:
#include "stm32f10x.h"
#define ADC_BUFFER_SIZE 100
uint16_t ADC_Buffer[ADC_BUFFER_SIZE];
void ADC1_Configuration(void) {
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
// 使能GPIOA、ADC1和DMA1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// 配置PA0为模拟输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置DMA1通道1
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_Buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = ADC_BUFFER_SIZE;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
// 使能DMA1通道1
DMA_Cmd(DMA1_Channel1, ENABLE);
// ADC1配置
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
// 配置ADC1通道0的采样时间
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_28Cycles5);
// 使能ADC1的DMA请求
ADC_DMACmd(ADC1, ENABLE);
// 使能ADC1
ADC_Cmd(ADC1, ENABLE);
// 校准ADC
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));
// 启动连续转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
STM32F103C8T6 内置 2 个 12 位 DAC(DAC1 和 DAC2),可输出 0-3.3V 范围内的模拟电压。DAC 支持 8 位或 12 位模式,可通过软件触发或定时器触发。
以下是配置 DAC1(PA4)输出固定电压的代码:
#include "stm32f10x.h"
void DAC_Configuration(void) {
GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDef DAC_InitStructure;
// 使能GPIOA和DAC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
// 配置PA4为模拟输入模式(DAC1输出)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// DAC1配置
DAC_InitStructure.DAC_Trigger = DAC_Trigger_None; // 无触发,软件控制
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; // 不生成波形
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bits11_0;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; // 使能输出缓冲
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
// 使能DAC1
DAC_Cmd(DAC_Channel_1, ENABLE);
}
void Set_DAC1_Value(uint16_t value) {
// 设置DAC1输出值(0-4095)
DAC_SetChannel1Data(DAC_Align_12b_R, value);
}
使用 DAC 和定时器生成正弦波:
#include "stm32f10x.h"
#include
#define SINE_POINTS 32 // 正弦波采样点数
#define PI 3.14159265358979f
uint16_t Sine_Table[SINE_POINTS];
void DAC_Configuration(void) {
GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDef DAC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
DMA_InitTypeDef DMA_InitStructure;
// 使能GPIOA、DAC和TIM6时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC | RCC_APB1Periph_TIM6, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// 配置PA4为模拟输入模式(DAC1输出)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 生成正弦波数据表
for (int i = 0; i < SINE_POINTS; i++) {
// 生成0-3.3V范围内的正弦波值(0-4095)
Sine_Table[i] = (uint16_t)(2047 + 2047 * sin(2.0f * PI * i / SINE_POINTS));
}
// 配置TIM6(用于触发DAC)
TIM_TimeBaseStructure.TIM_Period = 900; // 调整波形频率
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeInit(TIM6, &TIM_TimeBaseStructure);
// 设置TIM6为触发模式
TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);
// 配置DMA1通道2
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&DAC->DHR12R1;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Sine_Table;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = SINE_POINTS;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel2, &DMA_InitStructure);
// 使能DMA1通道2
DMA_Cmd(DMA1_Channel2, ENABLE);
// DAC1配置
DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO; // TIM6触发
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bits11_0;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
// 使能DAC1的DMA
DAC_DMACmd(DAC_Channel_1, ENABLE);
// 使能DAC1
DAC_Cmd(DAC_Channel_1, ENABLE);
// 启动TIM6
TIM_Cmd(TIM6, ENABLE);
}
下面是一个结合 ADC 和 DAC 的实例,实现简单的音频信号采集与回放系统:
#include "stm32f10x.h"
#include
#define BUFFER_SIZE 1024 // 缓冲区大小
#define SAMPLE_RATE 8000 // 采样率 (Hz)
uint16_t ADC_Buffer[BUFFER_SIZE]; // ADC采样缓冲区
uint16_t DAC_Buffer[BUFFER_SIZE]; // DAC输出缓冲区
uint8_t Buffer_Index = 0; // 缓冲区索引
uint8_t Is_Recording = 0; // 录音状态
uint8_t Is_Playing = 0; // 播放状态
void SystemInit(void);
void GPIO_Configuration(void);
void ADC_Configuration(void);
void DAC_Configuration(void);
void TIM_Configuration(void);
void DMA_Configuration(void);
void USART_Configuration(void);
void NVIC_Configuration(void);
int main(void) {
// 系统初始化
SystemInit();
// 外设配置
GPIO_Configuration();
USART_Configuration();
NVIC_Configuration();
TIM_Configuration();
ADC_Configuration();
DAC_Configuration();
DMA_Configuration();
// 启动定时器(触发ADC采样)
TIM_Cmd(TIM2, ENABLE);
printf("Audio Capture & Playback System\r\n");
printf("Type 'r' to start recording\r\n");
printf("Type 's' to stop recording\r\n");
printf("Type 'p' to start playback\r\n");
while (1) {
// 主循环处理用户命令
if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) {
char cmd = USART_ReceiveData(USART1);
switch (cmd) {
case 'r': // 开始录音
Is_Recording = 1;
Is_Playing = 0;
Buffer_Index = 0;
printf("Recording started...\r\n");
break;
case 's': // 停止录音
Is_Recording = 0;
printf("Recording stopped.\r\n");
break;
case 'p': // 开始播放
if (!Is_Recording) {
Is_Playing = 1;
Buffer_Index = 0;
printf("Playback started...\r\n");
// 启动DAC DMA
DMA_Cmd(DMA1_Channel3, ENABLE);
} else {
printf("Cannot play while recording!\r\n");
}
break;
default:
printf("Unknown command: %c\r\n", cmd);
printf("Valid commands: r, s, p\r\n");
break;
}
}
}
}
// TIM2中断处理函数(ADC采样触发)
void TIM2_IRQHandler(void) {
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// 如果正在录音,将ADC值存入缓冲区
if (Is_Recording) {
ADC_Buffer[Buffer_Index] = ADC_GetConversionValue(ADC1);
// 简单音量调整(除以2)
DAC_Buffer[Buffer_Index] = ADC_Buffer[Buffer_Index] / 2;
Buffer_Index++;
if (Buffer_Index >= BUFFER_SIZE) {
Buffer_Index = 0;
printf("Buffer full, looping...\r\n");
}
}
}
}
// 重定向printf函数到USART1
int fputc(int ch, FILE *f) {
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, (uint8_t)ch);
return ch;
}
参考电压:ADC 和 DAC 的参考电压通常为 VREF+(一般连接到 3.3V),决定了转换的满量程范围。
采样率:ADC 的最大采样率为 1MHz,实际应用中需根据系统时钟和预分频配置合理设置。
抗干扰:模拟信号输入端建议添加滤波电容,减少噪声干扰。
电源管理:模拟电路部分建议使用独立电源或 LC 滤波,避免数字电路噪声影响。
DMA 优化:对于大数据量的采集和输出,建议使用 DMA 以减少 CPU 负担。
分辨率考虑:12 位分辨率对应 4096 个量化级别,输入信号范围需匹配 ADC 量程。
通过合理配置 ADC 和 DAC,STM32F103C8T6 可以实现各种模拟信号处理应用,如传感器数据采集、波形生成、音频处理等。