以下是针对 Keil 编程的常用代码示例,涵盖 8051 单片机和 STM32(Cortex-M3)的基础功能,包括 LED 控制、按键检测、定时器、中断、串口通信等,可直接在 Keil 中创建项目并使用。

一、8051 单片机代码示例

1. LED 闪烁(基础 IO 操作)
#include "reg51.h"  // 8051 寄存器定义

// 定义 LED 引脚(假设接 P1.0)
sbit LED = P1^0;

// 简单延时函数(约 100ms,11.0592MHz 晶振)
void Delay100ms(void) {
    unsigned int i, j;
    for(i = 100; i > 0; i--)
        for(j = 110; j > 0; j--);
}

void main(void) {
    while(1) {
        LED = 0;  // 点亮 LED(低电平有效)
        Delay100ms();
        LED = 1;  // 熄灭 LED
        Delay100ms();
    }
}
2. 按键控制 LED(按键检测与消抖)
#include "reg51.h"

sbit LED = P1^0;    // LED 接 P1.0
sbit KEY = P3^2;    // 按键接 P3.2(上拉输入,按下为低电平)

// 延时函数(用于按键消抖,约 10ms)
void Delay10ms(void) {
    unsigned int i, j;
    for(i = 10; i > 0; i--)
        for(j = 110; j > 0; j--);
}

void main(void) {
    LED = 1;  // 初始熄灭
    while(1) {
        if(KEY == 0) {  // 检测到按键按下
            Delay10ms();  // 消抖
            if(KEY == 0) {  // 确认按下
                LED = ~LED;  // 翻转 LED 状态
                // 等待按键释放
                while(KEY == 0);
            }
        }
    }
}
3. 定时器中断实现 LED 定时闪烁(定时器 0)
#include "reg51.h"

sbit LED = P1^0;
unsigned int cnt = 0;  // 计数变量

// 定时器 0 初始化(1ms 中断一次,11.0592MHz 晶振)
void Timer0Init(void) {
    TMOD |= 0x01;  // 定时器 0 工作模式 1(16 位定时)
    TH0 = 0xFC;    // 初值装载(65536 - 1000 = 64536 = 0xFC18)
    TL0 = 0x18;
    ET0 = 1;       // 使能定时器 0 中断
    EA = 1;        // 使能总中断
    TR0 = 1;       // 启动定时器 0
}

// 定时器 0 中断服务函数
void Timer0_ISR(void) interrupt 1 {
    TH0 = 0xFC;  // 重新装载初值
    TL0 = 0x18;
    cnt++;
    if(cnt >= 1000) {  // 累计 1000ms = 1s
        cnt = 0;
        LED = ~LED;    // 翻转 LED 状态
    }
}

void main(void) {
    Timer0Init();
    while(1);  // 主循环空转,由中断处理逻辑
}
4. 串口通信(发送字符串,9600 波特率)
#include "reg51.h"
#include <string.h>

#define BAUD 9600  // 波特率
#define FOSC 11059200UL  // 晶振频率

// 串口初始化
void UART_Init(void) {
    SCON = 0x50;  // 工作模式 1(8 位 UART,可变波特率)
    // 计算定时器初值(模式 2 自动重装)
    TMOD &= 0x0F;  // 清除定时器 1 配置
    TMOD |= 0x20;  // 定时器 1 工作模式 2
    TH1 = 256 - (FOSC / (12 * 32 * BAUD));  // 波特率公式
    TL1 = TH1;
    TR1 = 1;  // 启动定时器 1
    ES = 1;   // 使能串口中断(可选,如需接收)
    EA = 1;   // 使能总中断
}

// 串口发送一个字符
void UART_SendChar(unsigned char c) {
    SBUF = c;         // 加载字符到发送缓冲区
    while(TI == 0);   // 等待发送完成
    TI = 0;           // 清除发送标志
}

// 串口发送字符串
void UART_SendString(unsigned char *str) {
    while(*str != '\0') {
        UART_SendChar(*str);
        str++;
    }
}

void main(void) {
    UART_Init();
    while(1) {
        UART_SendString("Hello 8051!\r\n");  // 发送字符串
        // 延时约 1s
        unsigned int i, j;
        for(i = 0; i < 1000; i++)
            for(j = 0; j < 112; j++);
    }
}

// 串口中断服务函数(如需接收数据)
void UART_ISR(void) interrupt 4 {
    if(RI) {         // 接收中断
        RI = 0;      // 清除接收标志
        // 可在此处理接收的数据(SBUF 为接收值)
    }
}

二、STM32(Cortex-M3,以 STM32F103 为例)代码示例

1. LED 闪烁(使用标准库)
#include "stm32f10x.h"

// 延时函数(简单循环,实际开发用定时器)
void DelayMs(uint32_t ms) {
    uint32_t i, j;
    for(i = 0; i < ms; i++)
        for(j = 0; j < 7200; j++);  // 约 1ms(72MHz 主频)
}

// LED 初始化(假设接 PA0)
void LED_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  // 使能 PA 时钟
    
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;  // 推挽输出
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
}

int main(void) {
    LED_Init();
    while(1) {
        GPIO_SetBits(GPIOA, GPIO_Pin_0);  // 熄灭 LED(高电平有效)
        DelayMs(500);
        GPIO_ResetBits(GPIOA, GPIO_Pin_0);  // 点亮 LED
        DelayMs(500);
    }
}
2. 定时器中断(TIM2 定时 1s 翻转 LED)
#include "stm32f10x.h"

void LED_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
}

// 定时器 2 初始化(1ms 中断一次,72MHz 主频)
void TIM2_Init(void) {
    TIM_TimeBaseInitTypeDef TIM_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);  // 使能 TIM2 时钟
    
    // 定时 1ms:72MHz / (7200 * 10) = 1000Hz → 周期 1ms
    TIM_InitStruct.TIM_Prescaler = 7199;  // 预分频器:7200-1
    TIM_InitStruct.TIM_Period = 9;        // 自动重装载值:10-1
    TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_InitStruct);
    
    // 中断配置
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);  // 使能更新中断
    NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
    
    TIM_Cmd(TIM2, ENABLE);  // 启动定时器
}

// 定时器 2 中断服务函数
void TIM2_IRQHandler(void) {
    static uint16_t cnt = 0;
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);  // 清除中断标志
        cnt++;
        if(cnt >= 1000) {  // 累计 1000ms = 1s
            cnt = 0;
            GPIO_WriteBit(GPIOA, GPIO_Pin_0, 
                (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_0)));  // 翻转 LED
        }
    }
}

int main(void) {
    LED_Init();
    TIM2_Init();
    while(1);
}
3. 串口通信(USART1 发送/接收,115200 波特率)
#include "stm32f10x.h"
#include <stdio.h>

// 串口初始化(USART1,PA9 发送,PA10 接收)
void USART1_Init(uint32_t baudrate) {
    GPIO_InitTypeDef GPIO_InitStruct;
    USART_InitTypeDef USART_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
    
    // 配置 TX 引脚(PA9)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽输出
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 配置 RX 引脚(PA10)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;  // 浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 串口参数配置
    USART_InitStruct.USART_BaudRate = baudrate;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;  // 收发模式
    USART_Init(USART1, &USART_InitStruct);
    
    // 使能接收中断
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);  // 接收非空中断
    NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
    
    USART_Cmd(USART1, ENABLE);  // 启动串口
}

// 串口发送一个字符
void USART1_SendChar(uint8_t c) {
    USART_SendData(USART1, c);
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);  // 等待发送完成
}

// 串口发送字符串
void USART1_SendString(uint8_t *str) {
    while(*str) {
        USART1_SendChar(*str++);
    }
}

// 重定向 printf 到串口(需勾选 Use Micro LIB)
int fputc(int ch, FILE *f) {
    USART1_SendChar((uint8_t)ch);
    return ch;
}

// 串口 1 中断服务函数(接收数据并回传)
void USART1_IRQHandler(void) {
    uint8_t data;
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
        data = USART_ReceiveData(USART1);  // 读取接收数据
        USART1_SendChar(data);  // 回传数据( echo 功能)
    }
}

int main(void) {
    USART1_Init(115200);  // 初始化串口,波特率 115200
    while(1) {
        printf("Hello STM32!\r\n");  // 发送字符串
        // 延时约 1s
        uint32_t i, j;
        for(i = 0; i < 1000; i++)
            for(j = 0; j < 7200; j++);
    }
}

使用说明:

  1. 8051 示例:适用于 Keil C51 环境,需选择对应芯片(如 AT89C51),注意晶振频率对延时和波特率的影响。
  2. STM32 示例:适用于 Keil MDK-ARM 环境,需安装 STM32F1 固件库,项目中需添加启动文件(如 startup_stm32f10x_md.s)和系统文件(如 system_stm32f10x.c)。
  3. 调试建议:先通过软件仿真验证逻辑,再连接硬件调试;注意引脚定义需与实际电路匹配。

这些示例覆盖了嵌入式开发的核心基础功能,可作为扩展复杂项目的起点(如 PWM 控制、ADC 采样、I2C/SPI 通信等)。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐