你点击蓝字关注,回复“入门资料”获取单片机入门到高级开挂教程
开发板带你入门,我们带你飞
文 | 无际(微信:2777492857)
全文约6326字,阅读大约需要 15 分钟
长文预警,肝了4个多小时,消耗一杯瑞幸,盘软件架构,这篇文章就够了!
// 延时函数,用于产生一定的延迟
void delay(unsigned int count) {
unsigned int i;
while(count--) {
for(i = 0; i < 120; i++) {} // 空循环,用于产生延迟
}
}
void main() {
// 初始设置P1端口为输出模式,用于控制LED
P1 = 0xFF; // 将P1端口设置为高电平,关闭所有LED
while(1) { // 无限循环
P1 = 0x00; // 将P1端口设置为低电平,点亮所有LED
delay(500000); // 调用延时函数,延迟一段时间
P1 = 0xFF; // 将P1端口设置为高电平,关闭所有LED
delay(500000); // 再次调用延时函数,延迟相同的时间
}
}
// 定义信号灯的状态
typedef enum {
RED_LIGHT,
YELLOW_LIGHT,
GREEN_LIGHT
} TrafficLightState;
// 函数声明
void initializeTrafficLight(void);
void setTrafficLight(TrafficLightState state);
void delay(unsigned int milliseconds);
// 信号灯控制主函数
void main(void) {
initializeTrafficLight(); // 初始化交通信号灯
while(1) {
setTrafficLight(RED_LIGHT);
delay(5000); // 红灯亮5秒
setTrafficLight(YELLOW_LIGHT);
delay(2000); // 黄灯亮2秒
setTrafficLight(GREEN_LIGHT);
delay(5000); // 绿灯亮5秒
}
}
// 初始化交通信号灯的函数
void initializeTrafficLight(void) {
// 这里可以添加初始化代码,比如设置端口方向、默认状态等
// 假设P1端口连接了信号灯,初始状态为熄灭(高电平)
P1 = 0xFF;
}
// 设置交通信号灯状态的函数
void setTrafficLight(TrafficLightState state) {
switch(state) {
case RED_LIGHT:
// 设置红灯亮,其他灯灭
P1 = 0b11100000; // 假设低电平有效,这里设置P1.0为低电平,其余为高电平
break;
case YELLOW_LIGHT:
// 设置黄灯亮,其他灯灭
P1 = 0b11011000; // 设置P1.1为低电平,其余为高电平
break;
case GREEN_LIGHT:
// 设置绿灯亮,其他灯灭
P1 = 0b11000111; // 设置P1.2为低电平,其余为高电平
break;
default:
// 默认为熄灭所有灯
P1 = 0xFF;
break;
}
}
// 延时函数,参数是毫秒数
void delay(unsigned int milliseconds) {
unsigned int delayCount = 0;
while(milliseconds--) {
for(delayCount = 0; delayCount < 120; delayCount++) {
// 空循环,用于产生延时
}
}
}
// 定义不同的操作级别
typedef enum {
LEVEL_USER,
LEVEL_ADMIN,
LEVEL_SUPERUSER
} OperationLevel;
// 函数声明
void systemInit(void);
void performOperation(OperationLevel level);
void displayMessage(char* message);
// 系统初始化后的主循环
void main(void) {
systemInit(); // 系统初始化
// 模拟用户操作
performOperation(LEVEL_USER);
// 模拟管理员操作
performOperation(LEVEL_ADMIN);
// 模拟超级用户操作
performOperation(LEVEL_SUPERUSER);
while(1) {
// 主循环可以是空闲循环或者处理其他低优先级任务
}
}
// 系统初始化函数
void systemInit(void) {
// 初始化系统资源,如设置端口、中断等
// 这里省略具体的初始化代码
}
// 执行不同级别操作的函数
void performOperation(OperationLevel level) {
switch(level) {
case LEVEL_USER:
//用户操作具体代码
break;
case LEVEL_ADMIN:
//管理员操作具体代码
break;
case LEVEL_SUPERUSER:
//超级用户操作具体代码
break;
}
}
// 显示消息的函数
void displayMessage(char* message) {
// 这里省略了实际的显示代码,因为单片机通常没有直接的屏幕输出
// 消息可以通过LED闪烁、串口输出或其他方式展示
// 假设通过P1端口的LED展示,每个字符对应一个LED闪烁模式
// 实际应用中,需要根据硬件设计来实现消息的显示
}
// 定义按键和LED的状态
// 函数声明
void delay(unsigned int milliseconds);
bit checkKeyPress(void); // 返回按键是否被按下的状态(1表示按下,0表示未按下)
// 定时器初始化函数
void timer0Init(void)
{
TMOD = 0x01; // 设置定时器模式寄存器,使用模式1(16位定时器)
TH0 = 0xFC; // 设置定时器初值,用于产生定时中断
TL0 = 0x18;
ET0 = 1; // 开启定时器0中断
EA = 1; // 开启总中断
TR0 = 1; // 启动定时器
}
// 定时器中断服务程序
void timer0_ISR() interrupt 1
{
// 定时器溢出后自动重新加载初值,无需手动重置
// 这里可以放置定时器溢出后需要执行的代码
}
// 按键中断服务程序
bit keyPress_ISR(void) interrupt 2 using 1
{
if(KEY_PORT != 0xFF) // 检测是否有按键按下
{
LED_PORT = ~LED_PORT; // 如果有按键按下,切换LED状态
delay(20); // 去抖动延时
while(KEY_PORT != 0xFF); // 等待按键释放
return 1; // 返回按键已按下
}
return 0; // 如果没有按键按下,返回0
}
// 延时函数,参数是毫秒数
void delay(unsigned int milliseconds) {
unsigned int i, j;
for(i = 0; i < milliseconds; i++)
for(j = 0; j < 1200; j++); // 空循环,用于产生延时
}
// 主函数
void main(void)
{
timer0Init(); // 初始化定时器
LED_PORT = 0xFF; // 初始LED熄灭(假设低电平点亮LED)
while(1)
{
if(checkKeyPress())
{ // 检查是否有按键按下事件
// 如果有按键按下,这里可以添加额外的处理代码
}
}
}
// 检查按键是否被按下的函数
bit checkKeyPress(void)
{
bit keyState = 0;
// 模拟按键中断触发,实际应用中需要连接硬件中断
if(1) // 假设按键中断触发
{
keyState = keyPress_ISR(); // 调用按键中断服务程序
}
return keyState; // 返回按键状态
}
// 定义自动售货机的状态
typedef enum {
IDLE,
COIN_INSERTED,
PRODUCT_SELECTED,
DISPENSE,
CHANGE_RETURNED
} VendingMachineState;
// 定义事件
typedef enum {
COIN_EVENT,
PRODUCT_EVENT,
DISPENSE_EVENT,
REFUND_EVENT
} VendingMachineEvent;
// 函数声明
void processEvent(VendingMachineEvent event);
void dispenseProduct(void);
void returnChange(void);
// 当前状态
VendingMachineState currentState = IDLE;
// 主函数
void main(void)
{
// 初始化代码(如果有)
// ...
while(1)
{
// 假设事件由外部触发,这里使用一个模拟事件
VendingMachineEvent currentEvent = COIN_EVENT; // 模拟投入硬币事件
processEvent(currentEvent); // 处理当前事件
}
}
// 处理事件的函数
void processEvent(VendingMachineEvent event)
{
switch(currentState)
{
case IDLE:
if(event == COIN_EVENT)
{
// 如果在空闲状态且检测到硬币投入事件,则转换到硬币投入状态
currentState = COIN_INSERTED;
}
break;
case COIN_INSERTED:
if(event == PRODUCT_EVENT)
{
// 如果在硬币投入状态且用户选择商品,则请求出货
currentState = PRODUCT_SELECTED;
}
break;
case PRODUCT_SELECTED:
if(event == DISPENSE_EVENT)
{
dispenseProduct(); // 出货商品
currentState = DISPENSE;
}
break;
case DISPENSE:
if(event == REFUND_EVENT)
{
returnChange(); // 返回找零
currentState = CHANGE_RETURNED;
}
break;
case CHANGE_RETURNED:
// 等待下一个循环,返回到IDLE状态
currentState = IDLE;
break;
default:
// 如果状态非法,重置为IDLE状态
currentState = IDLE;
break;
}
}
// 出货商品的函数
void dispenseProduct(void)
{
// 这里添加出货逻辑,例如激活电机推出商品
// 假设P1端口连接了出货电机
P1 = 0x00; // 激活电机
// ... 出货逻辑
P1 = 0xFF; // 关闭电机
}
// 返回找零的函数
void returnChange(void)
{
// 这里添加找零逻辑,例如激活机械臂放置零钱
// 假设P2端口连接了找零机械臂
P2 = 0x00; // 激活机械臂
// ... 找零逻辑
P2 = 0xFF; // 关闭机械臂
}
// 定义一个LED类
typedef struct {
unsigned char state; // LED的状态
unsigned char pin; // LED连接的引脚
void (*turnOn)(struct LED*); // 点亮LED的方法
void (*turnOff)(struct LED*); // 熄灭LED的方法
} LED;
// LED类的构造函数
void LED_Init(LED* led, unsigned char pin) {
led->state = 0; // 默认状态为熄灭
led->pin = pin; // 设置LED连接的引脚
}
// 点亮LED的方法
void LED_TurnOn(LED* led) {
// 根据引脚状态点亮LED
if(led->pin < 8) {
P0 |= (1 << led->pin); // 假设P0.0到P0.7连接了8个LED
} else {
P1 &= ~(1 << (led->pin - 8)); // 假设P1.0到P1.7连接了另外8个LED
}
led->state = 1; // 更新状态为点亮
}
// 熄灭LED的方法
void LED_TurnOff(LED* led) {
// 根据引脚状态熄灭LED
if(led->pin < 8) {
P0 &= ~(1 << led->pin); // 熄灭P0上的LED
} else {
P1 |= (1 << (led->pin - 8)); // 熄灭P1上的LED
}
led->state = 0; // 更新状态为熄灭
}
// 主函数
void main(void) {
LED myLed; // 创建一个LED对象
LED_Init(&myLed, 3); // 初始化LED对象,连接在P0.3
// 给LED对象绑定方法
myLed.turnOn = LED_TurnOn;
myLed.turnOff = LED_TurnOff;
// 使用面向对象的风格控制LED
while(1) {
myLed.turnOn(&myLed); // 点亮LED
// 延时
myLed.turnOff(&myLed); // 熄灭LED
// 延时
}
}
// 假设P1.0是LED输出
sbit LED = P1^0;
// 全局变量,用于记录系统Tick
unsigned int systemTick = 0;
// 任务函数声明
void taskLEDBlink(void);
void taskKeyScan(void);
// 定时器0中断服务程序,用于产生Tick
void timer0_ISR() interrupt 1 using 1
{
// 定时器溢出后自动重新加载初值,无需手动重置
systemTick++; // 更新系统Tick计数器
}
// 任务调度器,主函数中调用,负责任务轮询
void taskScheduler(void)
{
// 检查系统Tick,决定是否执行任务
// 例如,如果我们需要每1000个Tick执行一次LED闪烁任务
if (systemTick % 1000 == 0)
{
taskLEDBlink();
}
// 如果有按键任务,可以类似地检查Tick并执行
if (systemTick % 10 == 0)
{
taskKeyScan();
}
}
// LED闪烁任务
void taskLEDBlink(void)
{
static bit ledState = 0; // 用于记录LED的当前状态
ledState = !ledState; // 切换LED状态
LED = ledState; // 更新LED硬件状态
}
// 按键扫描任务(示例中省略具体实现)
void taskKeyScan(void)
{
// 按键扫描逻辑
}
// 主函数
void main(void)
{
// 初始化LED状态
LED = 0;
// 定时器0初始化设置
TMOD &= 0xF0; // 设置定时器模式寄存器,使用模式1(16位定时器/计数器)
TH0 = 0x4C; // 设置定时器初值,产生定时中断(定时周期取决于系统时钟频率)
TL0 = 0x00;
ET0 = 1; // 允许定时器0中断
EA = 1; // 允许中断
TR0 = 1; // 启动定时器0
while(1)
{
taskScheduler(); // 调用任务调度器
}
}
// 假设P3.5是按键输入,P1.0是LED输出
sbit KEY = P3^5;
sbit LED = P1^0;
typedef struct
{
unsigned char pin; // 代理关联的引脚
void (*action)(void); // 代理的行为函数
} Agent;
// 按键代理的行为函数声明
void keyAction(void);
// LED代理的行为函数声明
void ledAction(void);
// 代理数组,存储所有代理的行为和关联的引脚
Agent agents[] =
{
{5, keyAction}, // 按键代理,关联P3.5
{0, ledAction} // LED代理,关联P1.0
};
// 按键代理的行为函数
void keyAction(void)
{
if(KEY == 0) // 检测按键是否被按下
{
LED = !LED; // 如果按键被按下,切换LED状态
while(KEY == 0); // 等待按键释放
}
}
// LED代理的行为函数
void ledAction(void)
{
static unsigned int toggleCounter = 0;
toggleCounter++;
if(toggleCounter == 500) // 假设每500个时钟周期切换一次LED
{
LED = !LED; // 切换LED状态
toggleCounter = 0; // 重置计数器
}
}
// 主函数
void main(void)
{
unsigned char agentIndex;
// 主循环
while(1)
{
for(agentIndex = 0; agentIndex < sizeof(agents) / sizeof(agents[0]); agentIndex++)
{
// 调用每个代理的行为函数
(*agents[agentIndex].action)(); // 注意函数指针的调用方式
}
}
}
// 定义组件结构体
typedef struct
{
void (*init)(void); // 组件初始化函数
void (*task)(void); // 组件任务函数
} Component;
// 假设P3.5是按键输入,P1.0是LED输出
sbit KEY = P3^5;
sbit LED = P1^0;
// LED组件
void LED_Init(void)
{
LED = 0; // 初始化LED状态为关闭
}
void LED_Task(void)
{
static unsigned int toggleCounter = 0;
toggleCounter++;
if (toggleCounter >= 1000) // 假设每1000个时钟周期切换一次LED
{
LED = !LED; // 切换LED状态
toggleCounter = 0; // 重置计数器
}
}
// 按键组件
void KEY_Init(void)
{
// 按键初始化代码
}
void KEY_Task(void)
{
if (KEY == 0) // 检测按键是否被按下
{
LED = !LED; // 如果按键被按下,切换LED状态
while(KEY == 0); // 等待按键释放
}
}
// 组件数组,存储系统中所有组件的初始化和任务函数
Component components[] =
{
{LED_Init, LED_Task},
{KEY_Init, KEY_Task}
};
// 系统初始化函数,调用所有组件的初始化函数
void System_Init(void)
{
unsigned char componentIndex;
for (componentIndex = 0; componentIndex < sizeof(components) / sizeof(components[0]); componentIndex++)
{
components[componentIndex].init();
}
}
// 主循环,调用所有组件的任务函数
void main(void)
{
System_Init(); // 系统初始化
while(1)
{
unsigned char componentIndex;
for (componentIndex = 0; componentIndex < sizeof(components) / sizeof(components[0]); componentIndex++)
{
components[componentIndex].task(); // 调用组件任务
}
}
}
end
下面是更多无际原创的个人成长经历、行业经验、技术干货。
1.电子工程师是怎样的成长之路?10年5000字总结
2.如何快速看懂别人的代码和思维
3.单片机开发项目全局变量太多怎么管理?
4.C语言开发单片机为什么大多数都采用全局变量的形式?
5.单片机怎么实现模块化编程?实用程度让人发指!
6.c语言回调函数的使用及实际作用详解
7.手把手教你c语言队列实现代码,通俗易懂超详细!
8.c语言指针用法详解,通俗易懂超详细!