/**
* 实验一:1秒定时闪烁 - Wokwi适配版(带串口调试)
* 芯片:STM32C031C6 (Wokwi平台)
* 连接:LED正极 -> PA5, LED负极 -> GND
* 串口:PA2(TX), PA3(RX) - 使用USART2
*/
// 寄存器地址定义
#define RCC_BASE 0x40021000UL
#define RCC_IOPENR (*(volatile unsigned int*)(RCC_BASE + 0x34))
#define RCC_APBENR1 (*(volatile unsigned int*)(RCC_BASE + 0x3C))
#define RCC_APBENR2 (*(volatile unsigned int*)(RCC_BASE + 0x40))
#define GPIOA_BASE 0x50000000UL
#define GPIOA_MODER (*(volatile unsigned int*)(GPIOA_BASE + 0x00))
#define GPIOA_AFRL (*(volatile unsigned int*)(GPIOA_BASE + 0x20))
#define GPIOA_ODR (*(volatile unsigned int*)(GPIOA_BASE + 0x14))
#define USART2_BASE 0x40004400UL
#define USART2_CR1 (*(volatile unsigned int*)(USART2_BASE + 0x00))
#define USART2_CR2 (*(volatile unsigned int*)(USART2_BASE + 0x04))
#define USART2_BRR (*(volatile unsigned int*)(USART2_BASE + 0x0C))
#define USART2_ISR (*(volatile unsigned int*)(USART2_BASE + 0x1C))
#define USART2_TDR (*(volatile unsigned int*)(USART2_BASE + 0x28))
#define TIM3_BASE 0x40000400UL
#define TIM3_CR1 (*(volatile unsigned int*)(TIM3_BASE + 0x00))
#define TIM3_DIER (*(volatile unsigned int*)(TIM3_BASE + 0x0C))
#define TIM3_SR (*(volatile unsigned int*)(TIM3_BASE + 0x10))
#define TIM3_CNT (*(volatile unsigned int*)(TIM3_BASE + 0x24))
#define TIM3_PSC (*(volatile unsigned int*)(TIM3_BASE + 0x28))
#define TIM3_ARR (*(volatile unsigned int*)(TIM3_BASE + 0x2C))
// 位定义 - 请补充完整
#define RCC_IOPENR_GPIOAEN (1 << 0) // GPIOA时钟使能位
#define RCC_APBENR1_TIM3EN (1 << 1) // TIM3时钟使能位(填空)
#define RCC_APBENR2_USART2EN (1 << 0) // USART2时钟使能位(填空)
#define TIM_CR1_CEN (1 << 0) // 定时器使能位
#define TIM_DIER_UIE (1 << 0) // 更新中断使能位
#define TIM_SR_UIF (1 << 0) // 更新中断标志位
// USART位定义
#define USART_CR1_UE (1 << 0)
#define USART_CR1_TE (1 << 3)
#define USART_CR1_RE (1 << 2)
#define USART_ISR_TXE (1 << 7)
#define USART_ISR_TC (1 << 6)
// LED状态变量
volatile int led_state = 0;
volatile unsigned int tick_count = 0;
volatile unsigned int blink_counter = 0;
// 简单的延时函数
void simpleDelay(volatile unsigned int time) {
for (volatile unsigned int i = 0; i < time; i++) {
for (volatile unsigned int j = 0; j < 100; j++);
}
}
// 串口初始化函数
void USART2_Init(void) {
// 1. 使能USART2和GPIOA时钟
RCC_APBENR2 |= RCC_APBENR2_USART2EN;
RCC_IOPENR |= RCC_IOPENR_GPIOAEN;
simpleDelay(100);
// 2. 配置PA2(TX)为复用功能
GPIOA_MODER &= ~(3 << (2 * 2)); // 清除PA2模式
GPIOA_MODER |= (2 << (2 * 2)); // 复用功能模式
// 3. 配置PA3(RX)为复用功能(可选)
GPIOA_MODER &= ~(3 << (3 * 2));
GPIOA_MODER |= (2 << (3 * 2));
// 4. 配置复用功能为USART2 (AF1)
GPIOA_AFRL &= ~(0xF << (2 * 4)); // 清除PA2的AF
GPIOA_AFRL |= (1 << (2 * 4)); // AF1 for PA2 (USART2_TX)
GPIOA_AFRL &= ~(0xF << (3 * 4)); // 清除PA3的AF
GPIOA_AFRL |= (1 << (3 * 4)); // AF1 for PA3 (USART2_RX)
// 5. 配置USART2参数
// 波特率计算:48MHz / 9600 = 5000
USART2_BRR = 5000; // 9600波特率 @ 48MHz
// 6. 使能USART2,发送和接收
USART2_CR1 |= USART_CR1_UE; // 使能USART
USART2_CR1 |= USART_CR1_TE; // 使能发送
USART2_CR1 |= USART_CR1_RE; // 使能接收(可选)
simpleDelay(100);
}
// 串口发送单个字符
void USART2_SendChar(char c) {
// 等待发送缓冲区空
while (!(USART2_ISR & USART_ISR_TXE));
USART2_TDR = c;
}
// 串口发送字符串
void USART2_SendString(char* str) {
while (*str) {
USART2_SendChar(*str++);
}
}
// 串口发送数字(十进制)
void USART2_SendNumber(unsigned int num) {
char buffer[16];
int i = 0;
// 处理0的情况
if (num == 0) {
USART2_SendChar('0');
return;
}
// 将数字转换为字符串(逆序)
while (num > 0) {
buffer[i++] = '0' + (num % 10);
num /= 10;
}
// 逆序输出
while (i > 0) {
USART2_SendChar(buffer[--i]);
}
}
// 串口发送带格式的消息
void USART2_Printf(const char* format, ...) {
// 简化版,只支持基本字符串输出
// 实际项目中可以使用vsprintf等函数
USART2_SendString((char*)format);
}
// 发送调试信息
void sendDebugInfo(void) {
USART2_SendString("\r\n[调试] LED状态: ");
if (led_state) {
USART2_SendString("亮");
} else {
USART2_SendString("灭");
}
USART2_SendString(" | 计数: ");
USART2_SendNumber(blink_counter);
USART2_SendString(" | 定时器值: ");
USART2_SendNumber(TIM3_CNT);
USART2_SendString("\r\n");
}
void setup() {
// --- 1. 使能GPIOA时钟 ---
RCC_IOPENR |= RCC_IOPENR_GPIOAEN;
simpleDelay(100);
// --- 2. 配置PA5为输出模式 ---
GPIOA_MODER &= ~(3 << (5 * 2)); // 清除模式位
GPIOA_MODER |= (1 << (5 * 2)); // 设置为输出模式
// 初始状态:LED灭
GPIOA_ODR &= ~(1 << 5);
led_state = 0;
// --- 3. 初始化串口调试 ---
USART2_Init();
USART2_SendString("\r\n====================================\r\n");
USART2_SendString("STM32C031 LED闪烁程序 - 调试模式\r\n");
USART2_SendString("系统时钟: 48MHz\r\n");
USART2_SendString("定时器: TIM3, 1秒间隔\r\n");
USART2_SendString("LED引脚: PA5\r\n");
USART2_SendString("串口: 9600-8-N-1\r\n");
USART2_SendString("====================================\r\n\n");
// --- 4. 使能TIM3时钟 ---
RCC_APBENR1 |= RCC_APBENR1_TIM3EN;
simpleDelay(100);
// --- 5. 配置定时器参数(1秒定时)---
TIM3_PSC = 4800 - 1; // 48MHz/4800 = 10kHz (0.1ms/计数)
TIM3_ARR = 10000 - 1; // 计数10000次 = 1秒
TIM3_CNT = 0; // 从0开始计数
// --- 6. 清除中断标志并配置中断 ---
TIM3_SR &= ~TIM_SR_UIF; // 清除更新标志
TIM3_DIER |= TIM_DIER_UIE; // 使能更新中断
// --- 7. 启动定时器 ---
TIM3_CR1 |= TIM_CR1_CEN; // 使能计数器
USART2_SendString("定时器配置完成\r\n");
USART2_SendString("等待LED闪烁...\r\n\n");
// --- 8. 配置完成 ---
}
void loop() {
static unsigned int last_second = 0;
static unsigned int debug_counter = 0;
// 轮询方式检查定时器标志(不使用中断)
if (TIM3_SR & TIM_SR_UIF) {
// 清除中断标志
TIM3_SR &= ~TIM_SR_UIF;
// 翻转LED状态
if (led_state) {
GPIOA_ODR &= ~(1 << 5); // LED灭
led_state = 0;
USART2_SendString("[事件] LED熄灭\r\n");
} else {
GPIOA_ODR |= (1 << 5); // LED亮
led_state = 1;
USART2_SendString("[事件] LED点亮\r\n");
}
blink_counter++;
// 每10秒发送一次详细状态
if (blink_counter % 10 == 0) {
USART2_SendString("\r\n--- 状态更新 ---\r\n");
USART2_SendString("闪烁次数: ");
USART2_SendNumber(blink_counter);
USART2_SendString("\r\n系统运行时间: ");
USART2_SendNumber(blink_counter);
USART2_SendString(" 秒\r\n");
USART2_SendString("----------------\r\n");
}
}
// 每秒打印一个点表示程序正在运行(非阻塞)
unsigned int current_time = TIM3_CNT;
if (current_time > last_second + 5000) { // 大约0.5秒
last_second = current_time;
debug_counter++;
if (debug_counter % 2 == 0) { // 每秒打印一次
USART2_SendChar('.'); // 打印运行指示
}
}
}
// 注意:这个版本使用轮询方式而不是中断,这样在Wokwi中更可靠