diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000000000000000000000000000000000..38dfcd7b5f715d58daf6448820f4aff5e1e5be64 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: gcc.exe 生成活动文件", + "command": "C:\\mingw\\mingw64\\bin\\gcc.exe", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}\\${fileBasenameNoExtension}.exe" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "调试器生成的任务。" + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git "a/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/main..c" "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/main..c" new file mode 100644 index 0000000000000000000000000000000000000000..4f17332bd02ba4e85cb09c4250c5784f6834dbc9 --- /dev/null +++ "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/main..c" @@ -0,0 +1,71 @@ +#include +#include +#include + +#define THREAD_PRIORITY 25 +#define THREAD_STACK_SIZE 1024 +#define THREAD_TIMESLICE 10 + +static rt_thread_t tid1 = RT_NULL; +static rt_thread_t tid2 = RT_NULL; +static rt_thread_t tid3 = RT_NULL; + +static void thread1_entry(void *parameter) +{ + rt_uint32_t count = 0; + + while (1) + { + rt_kprintf("thread1 count: %d\n", count++); + rt_thread_mdelay(500); + } +} + +static void thread2_entry(void *parameter) +{ + rt_uint32_t count = 0; + + while (1) + { + rt_kprintf("thread2 count: %d\n", count++); + for (int i = 0; i < 1000000; i++); + } +} + +static void thread3_entry(void *parameter) +{ + rt_uint32_t count = 0; + + while (1) + { + rt_kprintf("THREAD3 COUNT: %d\n", count++); + rt_thread_mdelay(200); + } +} + +int main(void) +{ + tid1 = rt_thread_create("thread1",thread1_entry, RT_NULL,THREAD_STACK_SIZE,THREAD_PRIORITY + 2, THREAD_TIMESLICE); + if (tid1 != RT_NULL) + { + rt_thread_startup(tid1); + } + tid2 = rt_thread_create("thread2",thread2_entry, RT_NULL,THREAD_STACK_SIZE,THREAD_PRIORITY + 1, THREAD_TIMESLICE); + if (tid2 != RT_NULL) + { + rt_thread_startup(tid2); + } + tid3 = rt_thread_create("thread3",thread3_entry, RT_NULL,THREAD_STACK_SIZE,THREAD_PRIORITY, THREAD_TIMESLICE); + if (tid3 != RT_NULL) + { + rt_thread_startup(tid3); + } + + while (1) + { + rt_kprintf("main thread running\n"); + rt_thread_mdelay(1000); + } + + return RT_EOK; +} \ No newline at end of file diff --git "a/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\344\275\234\344\270\232.md" "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\344\275\234\344\270\232.md" new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git "a/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/event_demo.c" "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/event_demo.c" new file mode 100644 index 0000000000000000000000000000000000000000..e7fc8d451cb07f683da85443397556dc80a10281 --- /dev/null +++ "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/event_demo.c" @@ -0,0 +1,53 @@ +#include + +#define EVENT_FLAG3 (1 << 3) +#define EVENT_FLAG5 (1 << 5) + +static struct rt_event event; + +static void event_thread1(void *parameter) +{ + rt_uint32_t e; + while (1) + { + if (rt_event_recv(&event, (EVENT_FLAG3 | EVENT_FLAG5), + RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, + RT_WAITING_FOREVER, &e) == RT_EOK) + { + rt_kprintf("thread1: OR event received 0x%x\n", e); + } + } +} + +static void event_thread2(void *parameter) +{ + while (1) + { + rt_event_send(&event, EVENT_FLAG3); + rt_kprintf("thread2 send event 0x%x\n", EVENT_FLAG3); + rt_thread_mdelay(200); + + rt_event_send(&event, EVENT_FLAG5); + rt_kprintf("thread2 send event 0x%x\n", EVENT_FLAG5); + rt_thread_mdelay(500); + } +} + +int event_demo(void) +{ + rt_event_init(&event, "event", RT_IPC_FLAG_FIFO); + + rt_thread_t thread1 = rt_thread_create("thread1", + event_thread1, RT_NULL, + 512, 25, 5); + rt_thread_t thread2 = rt_thread_create("thread2", + event_thread2, RT_NULL, + 512, 26, 5); + + if (thread1 != RT_NULL) rt_thread_startup(thread1); + if (thread2 != RT_NULL) rt_thread_startup(thread2); + + return 0; +} + +MSH_CMD_EXPORT(event_demo, event demo); \ No newline at end of file diff --git "a/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/mailbox_demo.c" "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/mailbox_demo.c" new file mode 100644 index 0000000000000000000000000000000000000000..f9b11207299d38120e1721028f9f2864ac9f481c --- /dev/null +++ "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/mailbox_demo.c" @@ -0,0 +1,52 @@ +#include + +#define THREAD_PRIORITY 25 +#define MB_ENTRY_SIZE sizeof(rt_uint32_t) +#define MB_SIZE 4 + +static struct rt_mailbox mb; + +static void mailbox_send_thread(void *parameter) +{ + rt_uint32_t value = 0; + while (1) + { + value++; + if (rt_mb_send(&mb, (rt_uint32_t)value) == RT_EOK) + { + rt_kprintf("thread1 send mailbox: %d\n", value); + } + rt_thread_mdelay(500); + } +} + +static void mailbox_recv_thread(void *parameter) +{ + rt_uint32_t value; + while (1) + { + if (rt_mb_recv(&mb, (rt_ubase_t *)&value, RT_WAITING_FOREVER) == RT_EOK) + { + rt_kprintf("thread2 recv mailbox: %d\n", value); + } + } +} + +int mailbox_demo(void) +{ + rt_mb_init(&mb, "mbt", &mb[0], MB_SIZE, RT_IPC_FLAG_FIFO); + + rt_thread_t thread1 = rt_thread_create("thread1", + mailbox_send_thread, RT_NULL, + 512, THREAD_PRIORITY, 5); + rt_thread_t thread2 = rt_thread_create("thread2", + mailbox_recv_thread, RT_NULL, + 512, THREAD_PRIORITY, 5); + + if (thread1 != RT_NULL) rt_thread_startup(thread1); + if (thread2 != RT_NULL) rt_thread_startup(thread2); + + return 0; +} + +MSH_CMD_EXPORT(mailbox_demo, mailbox demo); \ No newline at end of file diff --git "a/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/message_queue_demo.c" "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/message_queue_demo.c" new file mode 100644 index 0000000000000000000000000000000000000000..d27a09603e0b6873530888e353f34b440b42bb53 --- /dev/null +++ "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/message_queue_demo.c" @@ -0,0 +1,63 @@ +#include + +#define THREAD_PRIORITY 25 +#define QUEUE_SIZE 5 +#define MSG_SIZE sizeof(struct msg) + +struct msg { + rt_uint8_t type; + rt_uint8_t data[20]; +}; + +static struct rt_messagequeue mq; + +static void mq_send_thread(void *parameter) +{ + struct msg buf; + buf.type = 0x01; + rt_memcpy(buf.data, "hello", 6); + + while (1) + { + if (rt_mq_send(&mq, &buf, MSG_SIZE) == RT_EOK) + { + rt_kprintf("thread1 send message: %s\n", buf.data); + } + rt_thread_mdelay(800); + } +} + +static void mq_recv_thread(void *parameter) +{ + struct msg buf; + while (1) + { + if (rt_mq_recv(&mq, &buf, MSG_SIZE, RT_WAITING_FOREVER) == RT_EOK) + { + rt_kprintf("thread2 recv message: type=%d data=%s\n", buf.type, buf.data); + } + } +} + +int message_queue_demo(void) +{ + rt_uint8_t msg_pool[QUEUE_SIZE * (MSG_SIZE + 4)]; + + rt_mq_init(&mq, "mqt", msg_pool, + MSG_SIZE, sizeof(msg_pool), + RT_IPC_FLAG_FIFO); + + rt_thread_t thread1 = rt_thread_create("thread1", + mq_send_thread, RT_NULL, + 512, THREAD_PRIORITY, 5); + rt_thread_t thread2 = rt_thread_create("thread2", + mq_recv_thread, RT_NULL, + 512, THREAD_PRIORITY, 5); + + if (thread1 != RT_NULL) rt_thread_startup(thread1); + if (thread2 != RT_NULL) rt_thread_startup(thread2); + + return 0; +} + +MSH_CMD_EXPORT(message_queue_demo, message queue demo); \ No newline at end of file diff --git "a/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/mutex_demo.c" "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/mutex_demo.c" new file mode 100644 index 0000000000000000000000000000000000000000..9e9b69f69fa31422ba2244f9fc37507faf4677a0 --- /dev/null +++ "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/mutex_demo.c" @@ -0,0 +1,50 @@ +#include + +static rt_mutex_t mutex; +static rt_uint32_t counter; + +static void mutex_thread1(void *parameter) +{ + while (1) + { + rt_mutex_take(mutex, RT_WAITING_FOREVER); + rt_kprintf("thread1: counter = %d\n", ++counter); + rt_mutex_release(mutex); + rt_thread_mdelay(10); + } +} + +static void mutex_thread2(void *parameter) +{ + while (1) + { + rt_mutex_take(mutex, RT_WAITING_FOREVER); + rt_kprintf("thread2: counter = %d\n", ++counter); + rt_mutex_release(mutex); + rt_thread_mdelay(10); + } +} + +int mutex_demo(void) +{ + mutex = rt_mutex_create("mtx", RT_IPC_FLAG_FIFO); + if (mutex == RT_NULL) + { + rt_kprintf("create mutex failed.\n"); + return -1; + } + + rt_thread_t thread1 = rt_thread_create("thread1", + mutex_thread1, RT_NULL, + 512, 25, 5); + rt_thread_t thread2 = rt_thread_create("thread2", + mutex_thread2, RT_NULL, + 512, 25, 5); + + if (thread1 != RT_NULL) rt_thread_startup(thread1); + if (thread2 != RT_NULL) rt_thread_startup(thread2); + + return 0; +} + +MSH_CMD_EXPORT(mutex_demo, mutex demo); \ No newline at end of file diff --git "a/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/semaphore_demo.c" "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/semaphore_demo.c" new file mode 100644 index 0000000000000000000000000000000000000000..ea7adc051f1ef70b474bba5035deab7b3e95f173 --- /dev/null +++ "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/semaphore_demo.c" @@ -0,0 +1,51 @@ +#include + +#define THREAD_PRIORITY 25 +#define THREAD_TIMESLICE 5 + +static rt_sem_t dynamic_sem; + +static void semaphore_thread1(void *parameter) +{ + while (1) + { + rt_kprintf("thread1 try to take semaphore...\n"); + rt_sem_take(dynamic_sem, RT_WAITING_FOREVER); + rt_kprintf("thread1 take semaphore!\n"); + rt_thread_mdelay(500); + } +} + +static void semaphore_thread2(void *parameter) +{ + while (1) + { + rt_kprintf("thread2 release semaphore!\n"); + rt_sem_release(dynamic_sem); + rt_thread_mdelay(1000); + } +} + +int semaphore_demo(void) +{ + dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_FIFO); + if (dynamic_sem == RT_NULL) + { + rt_kprintf("create semaphore failed.\n"); + return -1; + } + + rt_thread_t thread1 = rt_thread_create("thread1", + semaphore_thread1, RT_NULL, + 512, THREAD_PRIORITY, THREAD_TIMESLICE); + rt_thread_t thread2 = rt_thread_create("thread2", + semaphore_thread2, RT_NULL, + 512, THREAD_PRIORITY, THREAD_TIMESLICE); + + if (thread1 != RT_NULL) rt_thread_startup(thread1); + if (thread2 != RT_NULL) rt_thread_startup(thread2); + + return 0; +} + +MSH_CMD_EXPORT(semaphore_demo, semaphore demo); \ No newline at end of file diff --git "a/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/signal_demo.c" "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/signal_demo.c" new file mode 100644 index 0000000000000000000000000000000000000000..5c61739a8ab704db5c2632b4a1553aaecc570c3e --- /dev/null +++ "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\270\211\345\244\251\344\275\234\344\270\232/signal_demo.c" @@ -0,0 +1,49 @@ +#include + +static void signal_handler(int sig) +{ + rt_kprintf("Received signal %d\n", sig); +} + +static void signal_thread(void *parameter) +{ + rt_thread_t tid = rt_thread_self(); + rt_signal_install(SIGUSR1, signal_handler); + rt_signal_unmask(SIGUSR1); + + while (1) + { + rt_kprintf("thread1 wait signal...\n"); + rt_thread_mdelay(1000); + } +} + +static void trigger_thread(void *parameter) +{ + rt_thread_t tid = rt_thread_find("thread1"); + if (tid == RT_NULL) return; + + while (1) + { + rt_thread_mdelay(2000); + rt_kprintf("thread2 send signal\n"); + rt_thread_kill(tid, SIGUSR1); + } +} + +int signal_demo(void) +{ + rt_thread_t thread1 = rt_thread_create("thread1", + signal_thread, RT_NULL, + 512, 25, 5); + rt_thread_t thread2 = rt_thread_create("thread2", + trigger_thread, RT_NULL, + 512, 26, 5); + + if (thread1 != RT_NULL) rt_thread_startup(thread1); + if (thread2 != RT_NULL) rt_thread_startup(thread2); + + return 0; +} + +MSH_CMD_EXPORT(signal_demo, signal demo); \ No newline at end of file diff --git "a/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\272\224\345\244\251\344\275\234\344\270\232/main.c" "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\272\224\345\244\251\344\275\234\344\270\232/main.c" new file mode 100644 index 0000000000000000000000000000000000000000..15852a66102cb569a564420b031a9dd65614ac56 --- /dev/null +++ "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\344\272\224\345\244\251\344\275\234\344\270\232/main.c" @@ -0,0 +1,243 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mqtt_config.h" + +#define DBG_TAG "MQTT_STUDY" +#define DBG_LVL DBG_LOG +#include + +/* 全局变量 */ +static struct mqtt_client client; +static rt_sem_t mqtt_connected; +static rt_mutex_t mqtt_mutex; +static volatile rt_bool_t network_ready = RT_FALSE; + +/* 网络状态回调 */ +static void netdev_status_callback(struct netdev *netdev) +{ + if (netdev->ops->get_info(netdev) != RT_EOK) { + return; + } + + if (netdev_is_up(netdev) { + if (!network_ready) { + LOG_I("Network is ready"); + network_ready = RT_TRUE; + } + } else { + if (network_ready) { + LOG_W("Network lost"); + network_ready = RT_FALSE; + } + } +} + +/* 初始化网络 */ +static void network_init(void) +{ + /* 注册网络状态回调 */ + netdev_set_status_callback(netdev_status_callback); + + /* 等待网络就绪 */ + while (!network_ready) { + LOG_W("Waiting for network..."); + rt_thread_mdelay(1000); + } + + LOG_I("Network initialized"); +} + +/* MQTT事件回调 */ +static void mqtt_event_callback(struct mqtt_client *c, void *userdata, + enum mqtt_event_type event) +{ + switch (event) { + case MQTT_EVENT_CONNECTED: + LOG_I("MQTT connected"); + rt_sem_release(mqtt_connected); + break; + + case MQTT_EVENT_DISCONNECTED: + LOG_W("MQTT disconnected"); + break; + + case MQTT_EVENT_PUBLISH: { + struct mqtt_message *msg = userdata; + + /* 分配内存存储消息 */ + char *payload = rt_malloc(msg->payload_len + 1); + if (payload == RT_NULL) { + LOG_E("No memory for MQTT message"); + break; + } + + /* 复制消息内容 */ + memcpy(payload, msg->payload, msg->payload_len); + payload[msg->payload_len] = '\0'; + + /* 处理服务器响应 */ + if (strstr(payload, "辛苦了") != NULL) { + LOG_I("Server response: %s", payload); + } + + /* 释放内存 */ + rt_free(payload); + break; + } + + default: + break; + } +} + +/* 创建学习报告JSON */ +static char *create_study_report(void) +{ + cJSON *root = cJSON_CreateObject(); + if (root == RT_NULL) { + LOG_E("Failed to create JSON object"); + return RT_NULL; + } + + /* 添加字段 */ + cJSON_AddStringToObject(root, "name", STUDENT_NAME); + cJSON_AddStringToObject(root, "study", STUDY_CONTENT); + + /* 序列化JSON */ + char *json_str = cJSON_PrintUnformatted(root); + cJSON_Delete(root); + + return json_str; +} + +/* 发布学习报告 */ +static void publish_study_report(void) +{ + rt_mutex_take(mqtt_mutex, RT_WAITING_FOREVER); + + /* 创建JSON数据 */ + char *json_str = create_study_report(); + if (json_str == RT_NULL) { + LOG_E("Failed to create study report"); + rt_mutex_release(mqtt_mutex); + return; + } + + LOG_I("Publishing report: %s", json_str); + + /* 发布消息 */ + int rc = mqtt_publish(&client, PUB_TOPIC_STUDY, json_str, + strlen(json_str), MQTT_QOS_1, RT_FALSE); + if (rc != 0) { + LOG_E("Publish failed: %d", rc); + } + + /* 释放资源 */ + cJSON_free(json_str); + rt_mutex_release(mqtt_mutex); +} + +/* 初始化MQTT客户端 */ +static void mqtt_client_init(void) +{ + int rc; + + /* 初始化MQTT客户端 */ + mqtt_client_init(&client, MQTT_CLIENT_ID, RT_NULL); + + /* 设置回调函数 */ + client.event_callback = mqtt_event_callback; + + /* 连接MQTT代理 */ + rc = mqtt_connect(&client, MQTT_BROKER_HOST, MQTT_BROKER_PORT, + RT_FALSE, MQTT_USERNAME, MQTT_PASSWORD); + if (rc != 0) { + LOG_E("MQTT connect failed: %d", rc); + return; + } + + /* 等待连接成功 */ + if (rt_sem_take(mqtt_connected, RT_WAITING_FOREVER) != RT_EOK) { + LOG_E("Wait for MQTT connection timeout"); + return; + } + + /* 订阅响应主题 */ + rc = mqtt_subscribe(&client, SUB_TOPIC_RESPONSE, MQTT_QOS_1); + if (rc != 0) { + LOG_E("Subscribe failed: %d", rc); + return; + } + + LOG_I("MQTT client initialized"); +} + +/* MQTT工作线程 */ +static void mqtt_thread_entry(void *parameter) +{ + /* 等待网络就绪 */ + while (!network_ready) { + rt_thread_mdelay(500); + } + + /* 初始化MQTT客户端 */ + mqtt_client_init(); + + /* 发布学习报告 */ + publish_study_report(); + + /* 主循环处理MQTT消息 */ + while (1) { + rt_mutex_take(mqtt_mutex, RT_WAITING_FOREVER); + mqtt_cycle(&client, 100); // 处理MQTT消息 + rt_mutex_release(mqtt_mutex); + rt_thread_mdelay(200); + } +} + +/* 初始化应用 */ +static int mqtt_study_init(void) +{ + rt_thread_t tid; + + /* 创建信号量 */ + mqtt_connected = rt_sem_create("mqtt_conn", 0, RT_IPC_FLAG_FIFO); + if (mqtt_connected == RT_NULL) { + LOG_E("Failed to create semaphore"); + return -RT_ENOMEM; + } + + /* 创建互斥锁 */ + mqtt_mutex = rt_mutex_create("mqtt_mutex", RT_IPC_FLAG_FIFO); + if (mqtt_mutex == RT_NULL) { + LOG_E("Failed to create mutex"); + rt_sem_delete(mqtt_connected); + return -RT_ENOMEM; + } + + /* 初始化网络 */ + network_init(); + + /* 创建MQTT线程 */ + tid = rt_thread_create("mqtt", mqtt_thread_entry, RT_NULL, + 2048, 25, 10); + if (tid != RT_NULL) { + rt_thread_startup(tid); + LOG_I("MQTT thread created"); + } else { + LOG_E("Failed to create MQTT thread"); + return -RT_ERROR; + } + + return RT_EOK; +} + +/* 导出到自动初始化 */ +INIT_APP_EXPORT(mqtt_study_init); \ No newline at end of file diff --git "a/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\345\233\233\345\244\251\344\275\234\344\270\232/main.c" "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\345\233\233\345\244\251\344\275\234\344\270\232/main.c" new file mode 100644 index 0000000000000000000000000000000000000000..2a0094bcdac8d62d4ea2e7a401395f36c185c6ac --- /dev/null +++ "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\344\275\234\344\270\232/\347\254\254\345\233\233\345\244\251\344\275\234\344\270\232/main.c" @@ -0,0 +1,331 @@ +#include +#include +#include // 用于rand() + +/* 虚拟传感器控制命令 */ +#define VIR_SENSOR_CTRL_PRINT_INFO 0x20 +#define VIR_SENSOR_CTRL_SET_DATA 0x21 +#define VIR_SENSOR_CTRL_GET_DATA 0x22 +#define VIR_SENSOR_CTRL_START_SIM 0x23 +#define VIR_SENSOR_CTRL_STOP_SIM 0x24 +#define VIR_SENSOR_CTRL_SET_RANGE 0x25 + +/* 虚拟传感器设备结构体 */ +struct vir_sensor_dev +{ + struct rt_device parent; // RT-Thread设备基类 + rt_uint32_t sensor_data; // 当前传感器数据 + const char *info; // 传感器描述信息 + rt_uint32_t min_value; // 数据最小值 + rt_uint32_t max_value; // 数据最大值 + rt_timer_t simulate_timer; // 数据模拟定时器 + rt_uint32_t sim_interval; // 模拟间隔(ms) + rt_int32_t sim_step; // 每次模拟的变化步长 +}; + +/* 设备操作接口实现 */ +static rt_err_t vir_sensor_init(rt_device_t dev) +{ + struct vir_sensor_dev *sensor = (struct vir_sensor_dev *)dev; + rt_kprintf("Virtual sensor [%s] initialized\n", dev->parent.name); + return RT_EOK; +} + +static rt_err_t vir_sensor_open(rt_device_t dev, rt_uint16_t oflag) +{ + rt_kprintf("Virtual sensor [%s] opened\n", dev->parent.name); + return RT_EOK; +} + +static rt_err_t vir_sensor_close(rt_device_t dev) +{ + rt_kprintf("Virtual sensor [%s] closed\n", dev->parent.name); + return RT_EOK; +} + +static rt_size_t vir_sensor_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + struct vir_sensor_dev *sensor = (struct vir_sensor_dev *)dev; + + if (buffer == RT_NULL || size < sizeof(rt_uint32_t)) { + return 0; + } + + *(rt_uint32_t *)buffer = sensor->sensor_data; + return sizeof(rt_uint32_t); +} + +static rt_size_t vir_sensor_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + struct vir_sensor_dev *sensor = (struct vir_sensor_dev *)dev; + + if (buffer == RT_NULL || size < sizeof(rt_uint32_t)) { + return 0; + } + + rt_uint32_t new_value = *(rt_uint32_t *)buffer; + + // 数据范围检查 + if (new_value < sensor->min_value) new_value = sensor->min_value; + if (new_value > sensor->max_value) new_value = sensor->max_value; + + sensor->sensor_data = new_value; + return sizeof(rt_uint32_t); +} + +/* 定时器回调函数 - 数据模拟 */ +static void sensor_simulate(void *parameter) +{ + struct vir_sensor_dev *sensor = (struct vir_sensor_dev *)parameter; + + // 在[-sim_step, +sim_step]范围内随机变化 + rt_int32_t change = (rt_rand() % (2 * sensor->sim_step + 1)) - sensor->sim_step; + rt_int32_t new_value = (rt_int32_t)sensor->sensor_data + change; + + // 确保数据在范围内 + if (new_value < (rt_int32_t)sensor->min_value) new_value = sensor->min_value; + if (new_value > (rt_int32_t)sensor->max_value) new_value = sensor->max_value; + + sensor->sensor_data = (rt_uint32_t)new_value; + + // 发送数据更新事件(可选) + // rt_device_report_event(&sensor->parent, RT_DEVICE_EVENT_UPDATE, &sensor->sensor_data); +} + +static rt_err_t vir_sensor_control(rt_device_t dev, int cmd, void *args) +{ + struct vir_sensor_dev *sensor = (struct vir_sensor_dev *)dev; + + switch (cmd) { + case VIR_SENSOR_CTRL_PRINT_INFO: + if (args) { + rt_kprintf("[%s] Info: %s - %s\n", dev->parent.name, sensor->info, (char *)args); + } else { + rt_kprintf("[%s] Info: %s\n", dev->parent.name, sensor->info); + } + break; + + case VIR_SENSOR_CTRL_SET_DATA: + if (args) { + rt_uint32_t new_value = *(rt_uint32_t *)args; + // 数据范围检查 + if (new_value < sensor->min_value) new_value = sensor->min_value; + if (new_value > sensor->max_value) new_value = sensor->max_value; + sensor->sensor_data = new_value; + } + break; + + case VIR_SENSOR_CTRL_GET_DATA: + if (args) { + *(rt_uint32_t *)args = sensor->sensor_data; + } + break; + + case VIR_SENSOR_CTRL_START_SIM: + if (sensor->simulate_timer) { + rt_timer_start(sensor->simulate_timer); + rt_kprintf("[%s] Simulation started\n", dev->parent.name); + } + break; + + case VIR_SENSOR_CTRL_STOP_SIM: + if (sensor->simulate_timer) { + rt_timer_stop(sensor->simulate_timer); + rt_kprintf("[%s] Simulation stopped\n", dev->parent.name); + } + break; + + case VIR_SENSOR_CTRL_SET_RANGE: + if (args) { + rt_uint32_t *range = (rt_uint32_t *)args; + if (range[0] <= range[1]) { + sensor->min_value = range[0]; + sensor->max_value = range[1]; + // 确保当前值在新范围内 + if (sensor->sensor_data < sensor->min_value) + sensor->sensor_data = sensor->min_value; + if (sensor->sensor_data > sensor->max_value) + sensor->sensor_data = sensor->max_value; + } + } + break; + + default: + return -RT_ENOSYS; + } + return RT_EOK; +} + +/* 设备注册函数 */ +int rt_hw_vir_sensor_register(struct vir_sensor_dev *sensor, + const char *name, + const char *info, + rt_uint32_t init_value, + rt_uint32_t min_val, + rt_uint32_t max_val, + rt_uint32_t sim_interval, + rt_int32_t sim_step) +{ + RT_ASSERT(sensor != RT_NULL); + + // 初始化设备参数 + rt_memset(sensor, 0, sizeof(struct vir_sensor_dev)); + sensor->sensor_data = init_value; + sensor->info = info; + sensor->min_value = min_val; + sensor->max_value = max_val; + sensor->sim_interval = sim_interval; + sensor->sim_step = sim_step; + + // 设置设备操作接口 + sensor->parent.type = RT_Device_Class_Sensor; + sensor->parent.init = vir_sensor_init; + sensor->parent.open = vir_sensor_open; + sensor->parent.close = vir_sensor_close; + sensor->parent.read = vir_sensor_read; + sensor->parent.write = vir_sensor_write; + sensor->parent.control= vir_sensor_control; + + // 创建模拟定时器 + if (sim_interval > 0 && sim_step != 0) { + char timer_name[RT_NAME_MAX]; + rt_snprintf(timer_name, sizeof(timer_name), "sim_%s", name); + + sensor->simulate_timer = rt_timer_create(timer_name, + sensor_simulate, + sensor, + sim_interval, + RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER); + if (sensor->simulate_timer) { + rt_kprintf("[%s] Simulation timer created (%dms interval)\n", name, sim_interval); + } + } + + // 注册设备到RT-Thread系统 + rt_device_register(&sensor->parent, name, RT_DEVICE_FLAG_RDWR); + + return RT_EOK; +} + +/* 快捷函数接口 */ +void vir_sensor_set(rt_device_t dev, rt_uint32_t data) +{ + rt_device_control(dev, VIR_SENSOR_CTRL_SET_DATA, &data); +} + +void vir_sensor_get(rt_device_t dev, rt_uint32_t *data) +{ + rt_device_control(dev, VIR_SENSOR_CTRL_GET_DATA, data); +} + +void vir_sensor_print(rt_device_t dev, const char *str) +{ + rt_device_control(dev, VIR_SENSOR_CTRL_PRINT_INFO, (void *)str); +} + +void vir_sensor_start_sim(rt_device_t dev) +{ + rt_device_control(dev, VIR_SENSOR_CTRL_START_SIM, RT_NULL); +} + +void vir_sensor_stop_sim(rt_device_t dev) +{ + rt_device_control(dev, VIR_SENSOR_CTRL_STOP_SIM, RT_NULL); +} + +void vir_sensor_set_range(rt_device_t dev, rt_uint32_t min_val, rt_uint32_t max_val) +{ + rt_uint32_t range[2] = {min_val, max_val}; + rt_device_control(dev, VIR_SENSOR_CTRL_SET_RANGE, range); +} + +/* 传感器实例定义和初始化 */ +#ifdef RT_USING_VIR_SENSOR0 +static struct vir_sensor_dev vir_sensor0; + +static int rt_hw_vir_sensor0_init(void) +{ + return rt_hw_vir_sensor_register(&vir_sensor0, + "vs0", + "Virtual Temperature Sensor", + 25, // 初始值 25°C + -40, // 最小值 -40°C + 125, // 最大值 125°C + 1000, // 1秒更新间隔 + 2); // 每次变化±2°C +} +INIT_DEVICE_EXPORT(rt_hw_vir_sensor0_init); +#endif + +#ifdef RT_USING_VIR_SENSOR1 +static struct vir_sensor_dev vir_sensor1; + +static int rt_hw_vir_sensor1_init(void) +{ + return rt_hw_vir_sensor_register(&vir_sensor1, + "vs1", + "Virtual Humidity Sensor", + 50, // 初始值 50% + 0, // 最小值 0% + 100, // 最大值 100% + 2000, // 2秒更新间隔 + 5); // 每次变化±5% +} +INIT_DEVICE_EXPORT(rt_hw_vir_sensor1_init); +#endif + +/* 应用示例 */ +static void sensor_app_thread_entry(void *parameter) +{ + rt_device_t temp_dev = rt_device_find("vs0"); + rt_device_t humi_dev = rt_device_find("vs1"); + + if (!temp_dev || !humi_dev) { + rt_kprintf("Sensor devices not found!\n"); + return; + } + + rt_device_open(temp_dev, RT_DEVICE_FLAG_RDWR); + rt_device_open(humi_dev, RT_DEVICE_FLAG_RDWR); + + // 设置温度范围 + vir_sensor_set_range(temp_dev, -20, 80); + + // 启动模拟 + vir_sensor_start_sim(temp_dev); + vir_sensor_start_sim(humi_dev); + + while (1) { + rt_uint32_t temp, humi; + + // 读取传感器数据 + vir_sensor_get(temp_dev, &temp); + vir_sensor_get(humi_dev, &humi); + + rt_kprintf("Temperature: %d°C, Humidity: %d%%\n", temp, humi); + + // 随机设置湿度值 + if (rt_rand() % 10 == 0) { + rt_uint32_t new_humi = rt_rand() % 100; + vir_sensor_set(humi_dev, new_humi); + rt_kprintf("Set humidity to: %d%%\n", new_humi); + } + + rt_thread_mdelay(3000); + } +} + +static int sensor_app_init(void) +{ + rt_thread_t tid = rt_thread_create("sensor_app", + sensor_app_thread_entry, + RT_NULL, + 2048, + 20, + 10); + if (tid) { + rt_thread_startup(tid); + } + return RT_EOK; +} +INIT_APP_EXPORT(sensor_app_init); \ No newline at end of file diff --git "a/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\347\254\224\350\256\260/\347\254\254\344\270\200\345\244\251\347\254\224\350\256\260.md" "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\347\254\224\350\256\260/\347\254\254\344\270\200\345\244\251\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..f2ddf28888007b7e7de2dbc0b84a835d27afcbb7 --- /dev/null +++ "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\347\254\224\350\256\260/\347\254\254\344\270\200\345\244\251\347\254\224\350\256\260.md" @@ -0,0 +1,180 @@ +# 2025 RT-Thread 夏令营第一天笔记 + +## 上午 + +### 一、开发环境配置及示例工程的运行 + +#### 1.1 安装 Git 并抓取源码 +首先在电脑上安装 Git。然后打开命令行,输入以下命令来抓取 RT-Thread 源码和 ENV 环境: +```bash +git clone https://gitee.com/mirrors_RT-Thread/rt-thread.git +git clone --recursive --depth 1 https://gitee.com/mirrors_RT-Thread/env-windows.git +``` + +#### 1.2 配置 ENV 环境 +1. 打开 env-windows 文件夹,并运行 env.bat +2. 使用快捷键 Win + Alt + P 打开设置界面 +3. 在左侧点击 Integration,然后点击 Register 完成对 ENV 环境的配置与注册 +4. 注册完成后,在任意文件夹中右键菜单会出现 ConEmu Here 选项 + +#### 1.3 编译并运行 RT-Thread 示例工程 +1. 进入路径 `\rt-thread\bsp\qemu-vexpress-a9`,右键选择 ConEmu Here 打开终端 +2. 输入以下命令: +```bash +menuconfig +``` +回车后保存并退出,生成 Config 文件 +3. 更新 ENV 环境: +```bash +pkgs --upgrade +``` +4. 编译工程: +```bash +scons -j4 +``` +5. 编译完成后运行: +```bash +qemu-nographic.bat +``` +6. 再次进入 menuconfig,配置并下载 lvgl 软件包 +7. 重新编译后输入: +```bash +qemu +``` +即可成功运行 lvgl 示例工程 + +#### 1.4 安装 VS Code 并进行开发 +1. 下载并安装 Visual Studio Code +2. 使用 VS Code 打开 qemu-vexpress-a9 文件夹 +3. 找到 application 文件夹,编辑主函数 main.c +4. 修改完成后重复 1.3 中的编译和运行步骤,即可在终端看到运行结果 + +## 下午 + +### 二、Git 的学习和使用 + +#### 2.1 使用 Git 抓取代码 +在命令行中使用以下命令从 Gitee 抓取仓库代码: +```bash +git clone [仓库地址] +``` + +#### 2.2 使用 VS Code 和 Git Graph 插件 +1. 在 VS Code 中安装 Git Graph 插件 +2. 该插件可以避免使用命令行,简化 Git 操作,提高效率 +3. 在 Gitee 上 Fork RSOC-RTT 到自己的仓库 +4. 使用 SSH 方式将代码拉取到本地 +5. 创建自己的分支,编写笔记和作业 +6. 最后通过 Pull Request(PR)提交到远端仓库 + +### 三、常用的 Git 指令 + +#### 初始化仓库 +```bash +git init +``` +初始化一个新的 Git 仓库 + +#### 克隆仓库 +```bash +git clone [url] +``` +从远程仓库克隆一个项目到本地 + +#### 查看状态 +```bash +git status +``` +显示工作目录和暂存区的状态 + +#### 添加文件到暂存区 +```bash +git add [file] +``` +将指定文件添加到暂存区 +```bash +git add . +``` +将所有改动添加到暂存区 + +#### 提交更改 +```bash +git commit -m "[message]" +``` +提交暂存区的更改,并附带提交信息 + +#### 查看提交历史 +```bash +git log +``` +显示项目的提交日志 +```bash +git log --oneline +``` +以简洁的一行格式显示提交日志 + +#### 分支操作 +```bash +git branch +``` +列出所有的本地分支 +```bash +git branch [branch-name] +``` +创建新分支 +```bash +git checkout [branch-name] +``` +切换到指定分支 +```bash +git checkout -b [branch-name] +``` +创建并切换到新的分支 + +#### 合并分支 +```bash +git merge [branch] +``` +将指定分支合并到当前分支 + +#### 远程操作 +```bash +git remote add [remote-name] [url] +``` +添加一个新的远程仓库 +```bash +git fetch [remote-name] +``` +从远程仓库获取最新更新而不自动合并 +```bash +git pull [remote-name] [branch] +``` +获取并合并远程仓库的更改到当前分支 +```bash +git push [remote-name] [branch] +``` +推送本地分支的更新到远程仓库 + +#### 查看差异 +```bash +git diff +``` +显示工作目录与暂存区之间的差异 +```bash +git diff --staged +``` +显示暂存区与最近一次提交之间的差异 + +#### 删除操作 +```bash +git reset [file] +``` +将指定文件从暂存区移除,但保留工作目录中的内容 +```bash +git reset [commit] +``` +回滚到特定的提交(请谨慎使用,可能会丢失未提交的更改) +```bash +git rm [file] +``` +从工作目录和暂存区中删除文件 \ No newline at end of file diff --git "a/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\347\254\224\350\256\260/\347\254\254\344\270\211\345\244\251\347\254\224\350\256\260.md" "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\347\254\224\350\256\260/\347\254\254\344\270\211\345\244\251\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..fc3b31bbadb97df816957198e5f240c07ad608a8 --- /dev/null +++ "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\347\254\224\350\256\260/\347\254\254\344\270\211\345\244\251\347\254\224\350\256\260.md" @@ -0,0 +1,98 @@ +# 2025 RT-Thread 夏令营第三天笔记 +## 一、线程间的同步 +### 1.1 信号量(Semaphore) +**概念**: +信号量是一种用于控制多个线程对共享资源访问的同步机制。它通常代表可用资源的数量,可以用于线程间的同步或资源管理。信号量有两种类型:二值信号量(只能取0或1)和计数信号量(非负整数)。 +**工作机制**: +- **创建**:初始化一个信号量时,会指定一个初始值(例如,初始资源数量)。 +- **获取(take)**:当一个线程需要访问共享资源时,它会尝试获取信号量。如果信号量的值大于0,则信号量减1,线程继续执行;如果信号量的值为0,则线程会被阻塞,直到信号量的值大于0(表示有资源可用)。 +- **释放(release)**:当线程使用完共享资源后,会释放信号量,信号量的值加1。如果有其他线程正在等待该信号量,则其中一个线程会被唤醒(取决于等待策略,如FIFO或优先级)。 +信号量常用于: +- 限制同时访问共享资源的线程数量(例如,限制同时访问一个硬件的线程数)。 +- 线程同步(例如,一个线程完成任务后通知另一个线程)。 + +**示例代码**:先打开*RT-Studio*,然后创建新的工程,然后在界面左侧栏选择*RT-Thread Settings*,然后在软件包的杂项软件包里选择*samples*然后打开*rt-thread*软件包内核实例,然后依次打开*semaphore*、*mutex*和*event*,然后还可以将注释更改为中文,然后*Ctrl+s*保存文件,然后编译,运行即可看到信号量是如何使用的了。 +### 1.2 互斥量(Mutex) +**概念**: +互斥量是一种特殊的二值信号量,用于实现互斥访问共享资源。与信号量不同,互斥量具有所有权概念,即只有获取互斥量的线程才能释放它。互斥量还支持优先级继承,以防止优先级反转问题。 +**工作机制**: +- **创建**:互斥量初始为可用状态(解锁状态)。 +- **获取(lock)**:线程尝试获取互斥量。如果互斥量当前未被占用,则线程成功获取互斥量(互斥量变为锁定状态),并成为互斥量的所有者。如果互斥量已被其他线程占用,则当前线程被阻塞,直到互斥量被释放。 +- **释放(unlock)**:只有拥有互斥量的线程才能释放它。释放后,互斥量变为解锁状态。如果有其他线程在等待该互斥量,则其中一个线程会被唤醒并获取互斥量。 +互斥量的特点: +- **所有权**:只有持有互斥量的线程才能释放它。 +- **优先级继承**:如果一个高优先级线程等待一个被低优先级线程占有的互斥量,则低优先级线程会暂时提升到高优先级,以防止中优先级线程抢占导致的高优先级线程被阻塞过久(即优先级反转问题)。 +互斥量用于保护共享资源,确保同一时间只有一个线程访问该资源。 + +**示例代码**:与信号量的相同。 +### 1.3 事件集(Event) +**概念**: +事件集是一种用于线程间同步的机制,允许一个线程等待多个事件的发生,并且可以等待多个事件的任意组合(如任意一个事件发生或所有事件发生)。每个事件由一个位表示(通常32位系统支持32个事件)。 +**工作机制**: +- **创建**:事件集被初始化为0(没有任何事件发生)。 +- **发送(send)**:线程可以设置事件集中的某些位(表示事件发生)。发送事件会唤醒正在等待这些事件的线程。 +- **接收(recv)**:线程可以等待事件集中的事件。在等待时,可以指定: + - 等待哪些事件(通过事件掩码)。 + - 等待条件:是等待所有指定事件(逻辑与)还是任意一个事件(逻辑或)。 + - 是否清除事件:在成功接收到事件后,是否清除事件标志(自动清零)。 + **示例代码**:与信号量的相同。 + ## 二、线程间的通信 + ### 2.1邮箱(Mailbox) +**概念**: +邮箱是一种用于线程间通信的机制,它允许线程发送一个固定大小的消息(通常是4字节,在32位系统上为一个指针或一个整数值)给另一个线程。邮箱中每个消息槽只能存放一条消息,当邮箱满时,发送线程可以选择等待或立即返回。 +**工作机制**: +1. **初始化**:创建一个邮箱,并指定邮箱容量(消息数量)和消息大小(RT-Thread中固定为4字节)。 +2. **发送消息**: + - 如果邮箱未满,消息被复制到邮箱的一个槽中,并唤醒等待接收的线程(如果有)。 + - 如果邮箱已满,发送线程可以选择挂起等待(阻塞)直到有空位,或者立即返回错误。 +3. **接收消息**: + - 如果邮箱中有消息,接收线程取出消息(复制到提供的缓冲区),并唤醒等待发送的线程(如果有)。 + - 如果邮箱为空,接收线程可以选择挂起等待(阻塞)直到有消息,或者立即返回错误。 +**关键特性**: +- 消息大小固定(通常为4字节,适合传递指针或整数)。 +- 可以传递一个指向更大数据块的指针,但需要确保数据在接收线程访问时仍然有效(通常需要动态分配内存,并约定释放责任)。 +- 支持超时机制。 +**典型应用**: +- 传递简单的整数值或状态标志。 +- 传递指针,指向动态分配的数据结构(需注意内存管理)。 +- 轻量级的事件通知。 +### 2.2消息队列(Message Queue) +**概念**: +消息队列是邮箱的扩展,允许发送不定长的消息(但每条消息有最大长度限制)。消息队列在内部维护一个消息缓冲区,每个消息由消息头和消息内容组成。发送时消息被复制到队列缓冲区,接收时再从缓冲区复制出来。 +**工作机制**: +1. **初始化**:创建消息队列时,需要指定队列中消息的最大数量、每条消息的最大长度,并分配相应的存储空间(通常是一个连续的内存块)。 +2. **发送消息**: + - 如果队列中有足够的空间存放新消息,则消息被复制到队列的缓冲区中,并唤醒等待接收的线程。 + - 如果队列空间不足,发送线程可以选择挂起等待(阻塞)直到有空间,或者立即返回错误。 +3. **接收消息**: + - 如果队列中有消息,接收线程将消息从队列缓冲区复制到用户提供的缓冲区(需要足够大),并唤醒等待发送的线程(如果有空间释放)。 + - 如果队列为空,接收线程可以选择挂起等待(阻塞)直到有消息,或者立即返回错误。 +**关键特性**: +- 支持可变长度消息(每条消息长度在初始化时设定的最大值内)。 +- 消息在传递过程中被复制,发送方和接收方操作的是不同的内存副本(避免了共享内存的同步问题)。 +- 支持超时机制。 +- 比邮箱更灵活,但开销也更大。 +**典型应用**: +- 传递长度可变的数据(如字符串、结构体等)。 +- 需要传递较大数据块的场景(通过传递指针,但需要注意内存管理)。 +- 多线程之间传递复杂消息。 +### 2.3信号(Signal) +**概念**: +信号是一种异步通信机制,类似于操作系统中的软中断。一个线程可以给另一个线程(或自身)发送信号,接收线程在收到信号后,将中断当前执行流程,转而去执行预先注册的信号处理函数。处理完成后,线程再返回到被中断的地方继续执行。 +**工作机制**: +1. **安装信号处理函数**:线程通过`rt_signal_install()`为特定信号注册处理函数。 +2. **阻塞/解除阻塞信号**:线程可以屏蔽某些信号(`rt_signal_mask()`)或解除屏蔽(`rt_signal_unmask()`)。 +3. **发送信号**:线程通过`rt_thread_kill()`(注意:函数名类似Unix的kill,但只是发送信号)向目标线程发送信号。 +4. **信号处理**: + - 当目标线程被调度运行时,如果收到信号且该信号未被屏蔽,则调用相应的处理函数。 + - 信号处理函数在目标线程的上下文中执行(类似于中断,但属于线程上下文)。 + - 信号处理函数应尽量简短,避免调用可能引起阻塞的函数。 +**关键特性**: +- 异步通知:信号可以在任何时候发送给目标线程。 +- 处理函数执行在目标线程的上下文:因此会中断目标线程的正常执行流程。 +- 支持标准信号(如SIGINT, SIGUSR1等),也可以自定义信号。 +- 线程可以屏蔽信号(暂时不处理)或解除屏蔽。 +**典型应用**: +- 中断线程的正常流程以处理异常情况。 +- 自定义事件通知(如超时提醒、外部事件等)。 +- 实现类似Unix风格的信号处理(如Ctrl+C中断)。 \ No newline at end of file diff --git "a/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\347\254\224\350\256\260/\347\254\254\344\272\214\345\244\251\347\254\224\350\256\260.md" "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\347\254\224\350\256\260/\347\254\254\344\272\214\345\244\251\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..621377b0bf49c67591be2effb1ce273005b94732 --- /dev/null +++ "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\347\254\224\350\256\260/\347\254\254\344\272\214\345\244\251\347\254\224\350\256\260.md" @@ -0,0 +1,60 @@ +# 2025 RT-Thread 夏令营第二天笔记 +## 一、RT-Thread STtudio安装与配置 +### 1.1 RT Studio和SDK资源包下载 + 到*RT-Thread*官网*https://www.rt-thread.org/download.html#download-rt-thread-studio*下载*RT Studio*,下载完成后正常安装即可。然后打开*RT Studio*,然后打开SDK资源管理器,然后选择*STM32F407*,选择最新版本后安装。之后再安装最新版本的*QEMU* 。 + ### 1.2新建工程并模拟运行QEMU + 选择基于开发板创建新工程,开发板选择*STM32F407-ATK-EXPLORER*,然后调制器选择*QEMU*,待创建完成之后build工程即可。然后在工具栏选择下载,并选择与之对应的模拟器,然后系统控制台就会自动运行,然后输入*PC*即可查看当前任务状态。 +## 二、裸机和RTOS的区别 +### 2.1裸机的优势 +裸机适用于系统功能单一的情况下使用,并可以提高其运行效率;还可以使用较少的储存实现嵌入式系统的功能。 +### 2.2裸机的劣势 +裸机的代码耦合度较高,复用性较差;而且不适合复杂的嵌入式系统;同时在编写的代码不合理时容易造成系统阻塞。 +### 2.3ROTS的优势 +相较于裸机代码耦合度低,复用性较好;而且可以使用任务划分的方式降低实现复杂嵌入式系统的代码逻辑;RTOS下的延时不会一直占用CPU,延时期间处理其他任务;使用RTOS提供的线程同步与信息传递会提高系统的实时性。 +### 2.3ROTS的劣势 +在小容量的嵌入式系统中,其本身会占据部分内存;而且在单一系统中,系统调度会增加额外的开销。 + ## 三、初识RT-Thread内核 +### 3.1临界区 +定义:访问共享资源的代码段,同一时间仅允许一个线程访问。一但被占用,其他线程就不能够再使用,只能等待。 +### 3.2阻塞式线程和非阻塞式线程 +阻塞式线程对于临界区只能等,期间不能执行其他任务;非阻塞式线程对于临界区,等待期间可以执行其他任务。 +### 3.3RT-Thread系统启动流程 +3.3.1 **启动文件初始化**: + ```assembly + Reset_Handler: + BL SystemInit ; 时钟配置 + BL entry ; 进入RT-Thread + ``` + +3.3.2. **软件初始化**: + ```c + rt_hw_interrupt_disable(); // 关中断 + rt_hw_board_init(); // 板级外设 + rt_show_version(); // 打印版本 + rt_system_timer_init(); // 硬件定时器 + rt_system_scheduler_init(); // 调度器 + ``` +3.3.3. **线程创建**: + ```c + rt_application_init(); // 主线程 + rt_thread_idle_init(); // 空闲线程(优先级最低) + ``` +3.3.4 **启动调度**: + ```c + rt_system_scheduler_start(); // 开始线程调度 + ``` +### 3.4 空闲线程 +- 优先级最低(通常31) +- 永不挂起 +- 功能: + - CPU使用率统计 + - 低功耗模式管理 + - 僵尸线程资源回收 +### 3.5 线程调度机制 +1. **优先级抢占**: + - 高优先级线程可抢占低优先级线程 + - 示例:中断服务程序唤醒高优先级线程 + +2. **时间片轮转**: + - 相同优先级线程轮流执行 + - 时间片用完触发切换 \ No newline at end of file diff --git "a/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\347\254\224\350\256\260/\347\254\254\344\272\224\345\244\251\347\254\224\350\256\260.md" "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\347\254\224\350\256\260/\347\254\254\344\272\224\345\244\251\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..3cb12bfd2e983e75296465845bc85f88f26a3d82 --- /dev/null +++ "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\347\254\224\350\256\260/\347\254\254\344\272\224\345\244\251\347\254\224\350\256\260.md" @@ -0,0 +1,218 @@ +# 2025 RT-Thread 夏令营第五天笔记 + +## 一、在 RT-Thread 项目中添加 MQTT 与 JSON 包 + +### 1.1添加 MQTT 软件包 + + + +方法一:通过 RT-Thread Studio 添加 + + 打开 RT-Thread Studio,在项目上右键选择 “RT-Thread Settings”,点击 “软件包中心(Package Center)”,搜索关键词 “mqtt”,选择合适的 MQTT 软件包(如 rtthread-mqtt 等常用软件包),点击 “添加并保存配置”,软件包会自动下载并集成到项目中。 + +方法二:通过 Kconfig 菜单配置(适合命令行) + + 打开终端,进入项目根目录,执行 “menuconfig” 命令启动配置界面(根据环境可能需要激活 Python 虚拟环境)。在界面中依次进入 “RT-Thread online packages → Network → MQTT 软件包”,勾选所需的 MQTT 软件包,保存并退出配置。然后执行 “scons --target=mdk5”(根据使用的工具链选择 mdk5、iar、gcc 等目标)重新生成工程文件。 + +### 1.2添加 JSON 软件包(如 cJSON) + +步骤与添加 MQTT 软件包相同。通过 RT-Thread Studio 添加时,在 RT-Thread Settings 中打开软件包中心,搜索 “cJSON” 或 “json”,添加如 cJSON 等 JSON 库,保存配置即可使用。 + +### 1.3配置字段示例(Kconfig 格式) + +在项目或模块的 Kconfig 文件中添加以下内容,支持配置 MQTT 发布字段: + + + +``` +config MQTT\_DEMO\_NAME + + string "MQTT publish name field" + + default "xiaolu" + + help + + Configure the name field for MQTT publish JSON data +``` + +### 1.4软件包 + +RT-Thread 的软件包市场目前拥有 800 多个软件包,生态丰富,在开发项目时可以先看看有没有现成的软件包可以使用。 + +### 1.5 Scons + +SCons 是一个用 Python 语言编写的开源构建工具,类似于 Make。RT-Thread 引入 SCons 的目的是简化复杂的 Makefile 和 IDE 配置,让开发者更专注于功能开发。 + + + +1. 核心文件 + +* SConstruct:一个工程通常只有一个,是 SCons 的主入口文件。 + +* SConscript:通常每个存放源代码的子目录都有一个,用于组织该目录下的源码。 + +* rtconfig.py:RT-Thread 为每个 BSP 创建的独立配置文件,用于指定编译器、编译选项、链接选项等。 + +1. 常用 Scons 命令 + + 在 BSP 工程根目录下的 Env 命令行窗口中执行以下命令: + +* `scons`:编译工程。首次执行为完整编译,后续执行则为增量编译,只编译修改过的文件。参数`scons -s`,编译时不会打印详细的内部命令,输出更简洁。 + +* `scons -c`:清除编译目标。此命令会删除执行 scons 时生成的临时文件和目标文件。 + +* `scons --target=XXX`:生成指定 IDE 的工程文件。示例:`scons --target=mdk5`(生成 Keil MDK5 工程 project.uvprojx)、`scons --target=iar`(生成 IAR 工程 project.eww)、`scons --target=vsc`(在 bsp/simulator 目录下生成 VSCode 工程)。注意:每次通过 menuconfig 修改了组件配置后,都需要执行此命令重新生成工程文件,以同步源码和配置的变更。 + +* `scons -jN`:开启 N 个线程进行并行编译,可显著加快在多核 CPU 上的编译速度。示例:`scons -j4`(使用 4 个线程编译)。提示:如果只想查看编译错误或警告,建议不使用 - j 参数,以免错误信息被并行输出打乱。 + +* `scons --dist`:构建工程框架。该命令会在 BSP 目录下生成一个 dist 目录,其中包含了 RT-Thread 源码和当前 BSP 的相关工程,去除了不相关的 BSP 和 libcpu,方便拷贝和分发。 + +### 1.6 工程配置与管理 + + + +1. 修改编译器配置 (rtconfig.py) + + rtconfig.py 文件用于配置编译器类型和编译参数。 + +* 切换编译器:修改 CROSS\_TOOL 变量的值,可选值为 'gcc'、'keil'、'iar' 等。 + +* 指定编译器路径:修改 EXEC\_PATH 变量的值为编译器的安装路径。 + +注意事项:编译器安装路径中不要包含中文或空格;在 Windows 下修改路径时,需将路径分隔符 \ 替换为 /,或者在字符串前加 r(如 r'D:\Keil\_v5'),或使用双反斜杠 \ 进行转义;若要永久修改编译器配置,需要注释掉 rtconfig.py 文件中通过 os.getenv 获取环境变量的部分,否则 Env 的默认配置会覆盖文件中的修改。 + + + +1. 添加 / 管理源码 (SConscript) + + 通过修改或创建 SConscript 文件来管理项目中的源文件。 + +* 批量添加文件:applications 目录下的 SConscript 文件默认使用`src = Glob('*.c')`,会自动将该目录下的所有.c 文件添加到项目中。因此,简单的应用代码可以直接放在此目录下。 + +* 创建自定义模块:在 BSP 目录下新建一个模块文件夹(如 hello),在 hello 文件夹中创建 hello.c、hello.h 和一个 SConscript 文件。 + +SConscript 文件内容示例: + + + +``` +from building import \* + +\# 获取当前目录 + +cwd = GetCurrentDir() + +\# 定义源文件列表 + +src = \[] + +\# 通过依赖宏决定是否添加源文件 + +if GetDepend('RT\_USING\_HELLO'): + + src += \['hello.c'] + +\# 定义一个名为'hello'的组件(Group) + +\# CPPPATH用于添加头文件路径 + +group = DefineGroup('hello', src, depend = \[''], CPPPATH = \[cwd]) + +Return('group') +``` + +这段脚本的核心是使用`GetDepend('RT_USING_HELLO')`判断 rtconfig.h 中是否定义了 RT\_USING\_HELLO 宏,如果定义了,才会将 hello.c 添加到编译中。 + +### 1.7 Kconfig + +为了能通过图形化界面控制 RT\_USING\_HELLO 这类宏,需要使用 Kconfig 文件。 + + + +1. 创建 / 修改 Kconfig 文件:在 BSP 根目录的 Kconfig 文件中添加模块的配置项。 + + + +``` +menu "hello module" + + config RT\_USING\_HELLO + + bool "Enable hello module" + + default y + +endmenu +``` + +运行 menuconfig:在 Env 中执行 menuconfig 命令,即可看到新增的 “hello module” 菜单,并可以勾选启用。保存与生效:保存配置后,RT\_USING\_HELLO 宏会自动写入 rtconfig.h 文件。此时,之前编写的 SConscript 逻辑就会生效。最后一步:务必执行`scons --target=mdk5`(或其他 IDE)来重新生成工程,使新的文件和配置在 IDE 中可见。 + + + +1. Kconfig 语法 + +* 配置项 (Config):`config`用于定义一个配置项,是 Kconfig 的基本单元。语法:`config <配置项名称>`,例如`config RT_USING_HEAP`。 + +* 数据类型:`bool`(布尔类型,在菜单中显示为复选框 \[\*])、`string`(字符串类型,允许用户输入文本)、`int`(整型)、`hex`(十六进制类型)。语法:`bool "提示信息"`,例如`bool "Enable heap"`。 + +* 属性:`default`为配置项设置默认值,语法:`default `,例如`default y`(布尔类型)或`default 1024`(整型);`help`或`---help---`为配置项提供帮助信息,用户在 menuconfig 中选中时可以看到;`prompt`设置配置项在菜单中显示的提示文本,如果没有 prompt,该项就是隐藏的,但仍可通过 select 关键字被其他项选中。 + +* 依赖关系:`depends on <表达式>`为正向依赖,表示当前配置项的出现依赖于另一个配置项被选中,如果依赖项未满足,当前项在菜单中将不可见或不可选,示例:`config RT_USING_TIMER_SOFT`依赖于`RT_USING_TIMER`,只有开启了硬件定时器,软定时器选项才可见;`select <配置项>`为反向依赖,表示如果选中了当前项,那么被 select 的配置项将被自动选中,无论它是否有其他依赖,示例:Finsh 组件 select 了`RT_USING_DEVICE`,因为 Finsh 的运行需要设备框架的支持,当你开启 Finsh 时,设备框架会自动开启。 + +* 菜单结构:`menu "菜单名称" ... endmenu`定义一个菜单,将一组相关的配置项组织在一起;`choice "选项名称" ... endchoice`定义一个单选组,其中的配置项只能选择一个,在菜单中显示为 (\*);`comment "注释信息"`在菜单中显示一行注释文本,通常用于分组标题或说明;`source "路径/Kconfig"`将另一个 Kconfig 文件包含进来,一起解析。 + +1. 添加自定义配置项 + + 假设要为一个名为 “my\_module” 的自定义模块添加配置开关。 + +* 创建 Kconfig 文件:在 my\_module 的目录下创建一个 Kconfig 文件。 + +* 编写配置内容: + + + +``` +\# file: my\_module/Kconfig + +menu "My Awesome Module" # 创建一个专属菜单 + + config RT\_USING\_MY\_MODULE + + bool "Enable my awesome module" # bool类型的开关 + + default y # 默认开启 + + help + + This enables the demonstration module, which can print "Hello, RT-Thread!". + + if RT\_USING\_MY\_MODULE # 如果模块被开启 + + config MY\_MODULE\_BUFFER\_SIZE + + int "Buffer size for the module" + + default 128 + + help + + Specify the buffer size in bytes. + + config MY\_MODULE\_GREETING\_MSG + + string "Greeting message" + + default "Hello, RT-Thread!" + + endif # 结束 if 块 + +endmenu # 结束菜单 +``` + + + +* 包含到主 Kconfig:在上一级目录(如 rt-thread/components/Kconfig)或 BSP 的 Kconfig 文件中,使用 source 命令将此文件包含进来:`source "my_module/Kconfig"`。 + +* 运行 menuconfig:在 Env 中运行 menuconfig,就能看到 “My Awesome Module” 菜单及其中的配置项了。 + diff --git "a/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\347\254\224\350\256\260/\347\254\254\345\233\233\345\244\251\347\254\224\350\256\260.md" "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\347\254\224\350\256\260/\347\254\254\345\233\233\345\244\251\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..1d90c7f04b6f0cba5b99ad27e036c9ba1d525e31 --- /dev/null +++ "b/2025/\347\254\2541\347\273\204(STM32H750-ART-PI)/\345\274\240\344\270\200\346\231\250/\347\254\224\350\256\260/\347\254\254\345\233\233\345\244\251\347\254\224\350\256\260.md" @@ -0,0 +1,14 @@ +# 2025 RT-Thread 夏令营第四天笔记 +## 一、IO设备模型介绍 +RT-Thread的IO设备模型是RT-Thread内核的核心组件之一,它为上层应用程序提供了一套统一的设备操作接口(API),使得应用程序可以以相同的方式访问不同类型的硬件设备,而不必关心底层硬件的具体差异。 +#### 1.1 核心概念 +- **设备(Device)**:在RT-Thread中,设备被抽象为一种内核对象,每个设备都有一个名称,并通过设备驱动与硬件关联。 +- **设备驱动(Driver)**:设备驱动是底层硬件操作的实现,负责将标准的设备操作(如读、写、控制)映射到具体的硬件操作。 +- **设备类型(Device Class)**:设备按照其功能特性进行分类,如字符设备、块设备、网络设备、传感器设备等。设备类型决定了设备支持的操作集合。 +- **设备操作接口(Operations)**:设备驱动通过一组预定义的操作函数(如init, open, read, write, control等)来提供服务。 +## 二、设备驱动框架介绍 +### 2.1核心定义 +设备驱动框架是操作系统内核中**标准化硬件交互**的软件层,提供: +- 应用层通过标准API访问硬件(如 `read()`/`write()`) +- 驱动开发者复用通用逻辑,专注硬件差异 +- 操作系统统一管理硬件资源 diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day3/\344\272\213\344\273\266.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day3/\344\272\213\344\273\266.c" new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day3/\344\272\222\346\226\245\351\207\217.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day3/\344\272\222\346\226\245\351\207\217.c" new file mode 100644 index 0000000000000000000000000000000000000000..de27089f4d7005189e9a544c58f2c216bd0f2255 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day3/\344\272\222\346\226\245\351\207\217.c" @@ -0,0 +1,48 @@ +#include + +static rt_mutex_t mutex; +static rt_uint32_t counter; + +static void mutex_thread1(void *parameter) +{ + while (1) + { + rt_mutex_take(mutex, RT_WAITING_FOREVER); + rt_kprintf("thread1: counter = %d\n", ++counter); + rt_mutex_release(mutex); + rt_thread_mdelay(10); + } +} + +static void mutex_thread2(void *parameter) +{ + while (1) + { + rt_mutex_take(mutex, RT_WAITING_FOREVER); + rt_kprintf("thread2: counter = %d\n", ++counter); + rt_mutex_release(mutex); + rt_thread_mdelay(10); + } +} + +int mutex_demo(void) +{ + mutex = rt_mutex_create("mtx", RT_IPC_FLAG_FIFO); + if (mutex == RT_NULL) + { + rt_kprintf("create mutex failed.\n"); + return -1; + } + + rt_thread_t thread1 = rt_thread_create("thread1", + mutex_thread1, RT_NULL, + 512, 25, 5); + rt_thread_t thread2 = rt_thread_create("thread2", + mutex_thread2, RT_NULL, + 512, 25, 5); + + if (thread1 != RT_NULL) rt_thread_startup(thread1); + if (thread2 != RT_NULL) rt_thread_startup(thread2); + + return 0; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day3/\344\277\241\345\217\267.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day3/\344\277\241\345\217\267.c" new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day3/\344\277\241\345\217\267\351\207\217.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day3/\344\277\241\345\217\267\351\207\217.c" new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day3/\346\266\210\346\201\257\351\230\237\345\210\227.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day3/\346\266\210\346\201\257\351\230\237\345\210\227.c" new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day3/\351\202\256\347\256\261.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day3/\351\202\256\347\256\261.c" new file mode 100644 index 0000000000000000000000000000000000000000..f9b11207299d38120e1721028f9f2864ac9f481c --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day3/\351\202\256\347\256\261.c" @@ -0,0 +1,52 @@ +#include + +#define THREAD_PRIORITY 25 +#define MB_ENTRY_SIZE sizeof(rt_uint32_t) +#define MB_SIZE 4 + +static struct rt_mailbox mb; + +static void mailbox_send_thread(void *parameter) +{ + rt_uint32_t value = 0; + while (1) + { + value++; + if (rt_mb_send(&mb, (rt_uint32_t)value) == RT_EOK) + { + rt_kprintf("thread1 send mailbox: %d\n", value); + } + rt_thread_mdelay(500); + } +} + +static void mailbox_recv_thread(void *parameter) +{ + rt_uint32_t value; + while (1) + { + if (rt_mb_recv(&mb, (rt_ubase_t *)&value, RT_WAITING_FOREVER) == RT_EOK) + { + rt_kprintf("thread2 recv mailbox: %d\n", value); + } + } +} + +int mailbox_demo(void) +{ + rt_mb_init(&mb, "mbt", &mb[0], MB_SIZE, RT_IPC_FLAG_FIFO); + + rt_thread_t thread1 = rt_thread_create("thread1", + mailbox_send_thread, RT_NULL, + 512, THREAD_PRIORITY, 5); + rt_thread_t thread2 = rt_thread_create("thread2", + mailbox_recv_thread, RT_NULL, + 512, THREAD_PRIORITY, 5); + + if (thread1 != RT_NULL) rt_thread_startup(thread1); + if (thread2 != RT_NULL) rt_thread_startup(thread2); + + return 0; +} + +MSH_CMD_EXPORT(mailbox_demo, mailbox demo); \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day4.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day4.c" new file mode 100644 index 0000000000000000000000000000000000000000..b84d26b37fc7567f3e3cfeaceea58188c5bbdb8e --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/Day4.c" @@ -0,0 +1,336 @@ +#include +#include +#include + +/* 虚拟LED控制命令 */ +#define VLED_CTRL_TOGGLE 0x30 +#define VLED_CTRL_SET_BRIGHTNESS 0x31 +#define VLED_CTRL_GET_BRIGHTNESS 0x32 +#define VLED_CTRL_SET_PATTERN 0x33 +#define VLED_CTRL_START_BLINK 0x34 +#define VLED_CTRL_STOP_BLINK 0x35 + +/* LED状态模式 */ +typedef enum { + LED_SOLID, // 常亮模式 + LED_BLINK, // 闪烁模式 + LED_BREATHE // 呼吸模式 +} led_mode_t; + +/* 虚拟LED设备结构体 */ +struct vir_led_dev +{ + struct rt_device parent; // RT-Thread设备基类 + rt_uint8_t brightness; // 当前亮度 (0-100) + rt_uint8_t max_brightness; // 最大亮度 + led_mode_t mode; // 当前模式 + const char *color; // LED颜色描述 + rt_timer_t effect_timer; // 特效定时器 + rt_uint32_t interval; // 特效间隔(ms) + rt_uint8_t pattern[8]; // 闪烁模式序列 + rt_uint8_t pattern_index; // 当前模式索引 + rt_bool_t is_active; // 设备激活状态 +}; + +/* 设备操作接口实现 */ +static rt_err_t vir_led_init(rt_device_t dev) +{ + struct vir_led_dev *led = (struct vir_led_dev *)dev; + rt_kprintf("Virtual LED [%s] initialized\n", dev->parent.name); + return RT_EOK; +} + +static rt_err_t vir_led_open(rt_device_t dev, rt_uint16_t oflag) +{ + struct vir_led_dev *led = (struct vir_led_dev *)dev; + led->is_active = RT_TRUE; + rt_kprintf("LED [%s] activated\n", dev->parent.name); + return RT_EOK; +} + +static rt_err_t vir_led_close(rt_device_t dev) +{ + struct vir_led_dev *led = (struct vir_led_dev *)dev; + led->is_active = RT_FALSE; + rt_kprintf("LED [%s] deactivated\n", dev->parent.name); + return RT_EOK; +} + +static rt_size_t vir_led_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + struct vir_led_dev *led = (struct vir_led_dev *)dev; + + if (buffer && size >= sizeof(rt_uint8_t)) { + *(rt_uint8_t *)buffer = led->brightness; + return sizeof(rt_uint8_t); + } + return 0; +} + +static rt_size_t vir_led_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + struct vir_led_dev *led = (struct vir_led_dev *)dev; + + if (buffer && size >= sizeof(rt_uint8_t)) { + rt_uint8_t new_brightness = *(rt_uint8_t *)buffer; + + // 亮度范围限制 + if (new_brightness > led->max_brightness) + new_brightness = led->max_brightness; + + led->brightness = new_brightness; + rt_kprintf("LED [%s] set brightness: %d\n", dev->parent.name, new_brightness); + return sizeof(rt_uint8_t); + } + return 0; +} + +/* 定时器回调函数 - LED特效处理 */ +static void led_effect_handler(void *parameter) +{ + struct vir_led_dev *led = (struct vir_led_dev *)parameter; + + if (!led->is_active) return; + + switch (led->mode) { + case LED_BLINK: + // 根据模式序列切换状态 + led->brightness = led->pattern[led->pattern_index] ? led->max_brightness : 0; + led->pattern_index = (led->pattern_index + 1) % 8; + break; + + case LED_BREATHE: + // 呼吸灯效果 + static rt_int8_t breathe_dir = 1; + if (led->brightness >= led->max_brightness) breathe_dir = -5; + if (led->brightness <= 10) breathe_dir = 5; + led->brightness += breathe_dir; + break; + + default: + // 常亮模式不需要处理 + break; + } +} + +static rt_err_t vir_led_control(rt_device_t dev, int cmd, void *args) +{ + struct vir_led_dev *led = (struct vir_led_dev *)dev; + + switch (cmd) { + case VLED_CTRL_TOGGLE: + led->brightness = (led->brightness > 0) ? 0 : led->max_brightness; + rt_kprintf("LED [%s] toggled: %s\n", + dev->parent.name, + led->brightness ? "ON" : "OFF"); + break; + + case VLED_CTRL_SET_BRIGHTNESS: + if (args) { + rt_uint8_t new_val = *(rt_uint8_t *)args; + led->brightness = (new_val > led->max_brightness) ? + led->max_brightness : new_val; + } + break; + + case VLED_CTRL_GET_BRIGHTNESS: + if (args) { + *(rt_uint8_t *)args = led->brightness; + } + break; + + case VLED_CTRL_SET_PATTERN: + if (args) { + rt_memcpy(led->pattern, args, sizeof(led->pattern)); + led->pattern_index = 0; + rt_kprintf("LED [%s] pattern set\n", dev->parent.name); + } + break; + + case VLED_CTRL_START_BLINK: + if (led->effect_timer) { + led->mode = LED_BLINK; + rt_timer_start(led->effect_timer); + } + break; + + case VLED_CTRL_STOP_BLINK: + if (led->effect_timer) { + rt_timer_stop(led->effect_timer); + led->mode = LED_SOLID; + led->brightness = 0; // 闪烁停止后关闭LED + } + break; + + default: + return -RT_ENOSYS; + } + return RT_EOK; +} + +/* 设备注册函数 */ +int rt_hw_vir_led_register(struct vir_led_dev *led, + const char *name, + const char *color, + rt_uint8_t max_brightness, + rt_uint32_t effect_interval) +{ + RT_ASSERT(led != RT_NULL); + + // 初始化设备参数 + rt_memset(led, 0, sizeof(struct vir_led_dev)); + led->color = color; + led->max_brightness = max_brightness; + led->mode = LED_SOLID; + + // 默认闪烁模式: 1秒间隔 (10101010) + for (int i = 0; i < 8; i++) { + led->pattern[i] = (i % 2) ? 1 : 0; + } + + // 设置设备操作接口 + led->parent.type = RT_Device_Class_Miscellaneous; + led->parent.init = vir_led_init; + led->parent.open = vir_led_open; + led->parent.close = vir_led_close; + led->parent.read = vir_led_read; + led->parent.write = vir_led_write; + led->parent.control= vir_led_control; + + // 创建特效定时器 + if (effect_interval > 0) { + char timer_name[RT_NAME_MAX]; + rt_snprintf(timer_name, sizeof(timer_name), "eff_%s", name); + + led->effect_timer = rt_timer_create(timer_name, + led_effect_handler, + led, + effect_interval, + RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER); + if (led->effect_timer) { + rt_kprintf("[%s] Effect timer created (%dms)\n", name, effect_interval); + } + } + + // 注册设备到RT-Thread系统 + rt_device_register(&led->parent, name, RT_DEVICE_FLAG_RDWR); + + return RT_EOK; +} + +/* 快捷函数接口 */ +void vir_led_toggle(rt_device_t dev) +{ + rt_device_control(dev, VLED_CTRL_TOGGLE, RT_NULL); +} + +void vir_led_set_brightness(rt_device_t dev, rt_uint8_t brightness) +{ + rt_device_control(dev, VLED_CTRL_SET_BRIGHTNESS, &brightness); +} + +rt_uint8_t vir_led_get_brightness(rt_device_t dev) +{ + rt_uint8_t brightness = 0; + rt_device_control(dev, VLED_CTRL_GET_BRIGHTNESS, &brightness); + return brightness; +} + +void vir_led_set_pattern(rt_device_t dev, rt_uint8_t pattern[8]) +{ + rt_device_control(dev, VLED_CTRL_SET_PATTERN, pattern); +} + +void vir_led_start_blink(rt_device_t dev) +{ + rt_device_control(dev, VLED_CTRL_START_BLINK, RT_NULL); +} + +void vir_led_stop_blink(rt_device_t dev) +{ + rt_device_control(dev, VLED_CTRL_STOP_BLINK, RT_NULL); +} + +/* LED实例定义和初始化 */ +#ifdef RT_USING_VLED_RED +static struct vir_led_dev vled_red; + +static int rt_hw_vled_red_init(void) +{ + return rt_hw_vir_led_register(&vled_red, + "vledr", + "Red LED", + 100, // 最大亮度 100% + 500); // 500ms特效间隔 +} +INIT_DEVICE_EXPORT(rt_hw_vled_red_init); +#endif + +#ifdef RT_USING_VLED_GREEN +static struct vir_led_dev vled_green; + +static int rt_hw_vled_green_init(void) +{ + return rt_hw_vir_led_register(&vled_green, + "vledg", + "Green LED", + 90, // 最大亮度 90% + 300); // 300ms特效间隔 +} +INIT_DEVICE_EXPORT(rt_hw_vled_green_init); +#endif + +/* 应用示例 */ +static void led_app_thread_entry(void *parameter) +{ + rt_device_t red_led = rt_device_find("vledr"); + rt_device_t green_led = rt_device_find("vledg"); + + if (!red_led || !green_led) { + rt_kprintf("LED devices not found!\n"); + return; + } + + rt_device_open(red_led, RT_DEVICE_FLAG_RDWR); + rt_device_open(green_led, RT_DEVICE_FLAG_RDWR); + + // 设置红色LED为呼吸模式 + struct vir_led_dev *red = (struct vir_led_dev *)red_led; + red->mode = LED_BREATHE; + rt_timer_start(red->effect_timer); + + // 设置绿色LED闪烁模式 + rt_uint8_t custom_pattern[8] = {1,1,0,0,1,0,1,0}; // 自定义闪烁模式 + vir_led_set_pattern(green_led, custom_pattern); + vir_led_start_blink(green_led); + + while (1) { + // 随机切换红色LED状态 + if (rt_rand() % 5 == 0) { + vir_led_toggle(red_led); + } + + // 随机调整绿色LED亮度 + if (rt_rand() % 8 == 0) { + rt_uint8_t new_brightness = rt_rand() % 100; + vir_led_set_brightness(green_led, new_brightness); + } + + rt_thread_mdelay(2000); + } +} + +static int led_app_init(void) +{ + rt_thread_t tid = rt_thread_create("led_app", + led_app_thread_entry, + RT_NULL, + 1024, + 25, + 10); + if (tid) { + rt_thread_startup(tid); + } + return RT_EOK; +} +INIT_APP_EXPORT(led_app_init); \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/main.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/main.c" new file mode 100644 index 0000000000000000000000000000000000000000..939336066cd9e6a17bb65fc51185ee65880e92e1 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\344\275\234\344\270\232/main.c" @@ -0,0 +1,67 @@ +#include +#include +#include + +#define thread_size 512 +#define thread_pri 15 +#define thread_tick 5 + + +#define THREAD1_TICK 10 +#define THREAD2_TICK 20 +#define THREAD3_TICK 30 + +uint16_t sum1, sum2; + + +void thread1_entry() +{ + while(1) + { + rt_kprintf("sum1=%d\n", sum1++); + rt_thread_mdelay(500); + } +} + +void thread2_entry() +{ + while(1) + { + rt_kprintf("sum2=%d\n", sum2++); + rt_thread_mdelay(500); + } +} + +void thread3_entry() +{ + while(1) + { + rt_kprintf("ing......\n"); + rt_thread_mdelay(300); + } +} + + +int main(void) +{ + rt_thread_t thread1 = RT_NULL; + rt_thread_t thread2 = RT_NULL; + rt_thread_t thread3 = RT_NULL; + + thread3 = rt_thread_create("thread3", thread3_entry, RT_NULL, + thread_size, thread_pri, THREAD3_TICK); + thread1 = rt_thread_create("thread1", thread1_entry, RT_NULL, + thread_size, thread_pri, THREAD1_TICK); + + thread2 = rt_thread_create("thread2", thread2_entry, RT_NULL, + thread_size, thread_pri, THREAD2_TICK); + + if(thread3) + rt_thread_startup(thread3); + if(thread1) + rt_thread_startup(thread1); + if(thread2) + rt_thread_startup(thread2); + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\347\254\224\350\256\260/Day1\347\254\224\350\256\260.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\347\254\224\350\256\260/Day1\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..49c7f297a22b291471718e85e2bb5f5c27a1bcfc --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\347\254\224\350\256\260/Day1\347\254\224\350\256\260.md" @@ -0,0 +1,30 @@ +# ENV工具使用 +```c +scons -4j//对文件进行编译 +pkgs --upgrade//升级软件包 +menuconfig//打开图形化配置界面 +``` +其中使用pkgs --upgrade时可能会报错如下 +![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20250722/e7d05ce56c526fcc60c8346d89eff8d0.png.webp) +此时需要进入ENV的这个目录:E:\env\tools\scripts\cmds,注意ENV安装的目录 +在这个目录下打开ENV工具,运行menuconfig,保存 + +vsc进行代码编写,在env工具进行编译后使用qemu-nographic.bat 运行编译好的文件 +![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20250722/6a1eb985c8edab33fe71d2828a403a0a.png.webp) + +![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20250722/63fc6d6d9f8b2a94616f28f942f7caae.png.webp) +使用qemu.bat运行lvgl的demo + +![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20250722/e8553b927d181517f5217ea3022c0554.png.webp) +#git使用 + +```c +git add 暂存 +git commit 提交 +git log 查看历史 +git checkout/git reset 回退 +git branch 创建分支 +git merge 合并分支 +git push/pull 推送/拉取 +git status 查看文件状态 +``` \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\347\254\224\350\256\260/Day2\347\254\224\350\256\260.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\347\254\224\350\256\260/Day2\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..0cd1925c9970769b1d54756f5ed1df65aa8d3a70 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\347\254\224\350\256\260/Day2\347\254\224\350\256\260.md" @@ -0,0 +1,111 @@ +# RTT Studio使用 +**1.资源包下载** + 安装STM32F407探索者和qemu的资源包 +**2.新建RTT项目** +**3.打开RT-Thread Settings下载示例** +# 基本知识 +**临界区**:每个进程中访问临界资源的那段程序称之为临界区 +**信号量**:表示可用资源的数量,即线程可访问 +**阻塞**:当程序执行某个操作时,若条件未满足,则主动挂起等待,直到条件满足后才继续执行。 +**非阻塞**:执行操作时无论是否满足条件都立即返回。 +# 创建线程以及应用 +**动态**: + +```c +rt_thread_create(1.线程名字 + 2.入口函数 + 3.入口函数参数 + 4.栈大小 + 5.线程优先级 + 6.线程时间片大小) +``` +**静态**: + +```c +rt_thread_init(1.线程控制块的地址 + 2.线程名字 + 3.入口函数 + 4.入口函数参数 + 5.线程栈起始地址 + 6.栈大小 + 7.线程优先级 + 8.线程时间片大小) +``` +两者区别在于静态的需要提前分配内存,在某些需要保证安全的情况下更合适使用 +**初始化线程**: + +```c +rt_thread_startup(线程名) +``` +**作业**: + +```c +#include +#include +#include + +#define thread_size 512 +#define thread_pri 15 +#define thread_tick 5 + + +#define THREAD1_TICK 10 +#define THREAD2_TICK 20 +#define THREAD3_TICK 30 + +uint16_t sum1, sum2; + + +void thread1_entry() +{ + while(1) + { + rt_kprintf("sum1=%d\n", sum1++); + rt_thread_mdelay(500); + } +} + +void thread2_entry() +{ + while(1) + { + rt_kprintf("sum2=%d\n", sum2++); + rt_thread_mdelay(500); + } +} + +void thread3_entry() +{ + while(1) + { + rt_kprintf("ing......\n"); + rt_thread_mdelay(300); + } +} + + +int main(void) +{ + rt_thread_t thread1 = RT_NULL; + rt_thread_t thread2 = RT_NULL; + rt_thread_t thread3 = RT_NULL; + + thread3 = rt_thread_create("thread3", thread3_entry, RT_NULL, + thread_size, thread_pri, THREAD3_TICK); + thread1 = rt_thread_create("thread1", thread1_entry, RT_NULL, + thread_size, thread_pri, THREAD1_TICK); + + thread2 = rt_thread_create("thread2", thread2_entry, RT_NULL, + thread_size, thread_pri, THREAD2_TICK); + + if(thread3) + rt_thread_startup(thread3); + if(thread1) + rt_thread_startup(thread1); + if(thread2) + rt_thread_startup(thread2); + + return RT_EOK; +} + +``` \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\347\254\224\350\256\260/Day3\347\254\224\350\256\260.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\347\254\224\350\256\260/Day3\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..ffc21a7a8c3490c811e8e5bc71ad900376d26bef --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\347\254\224\350\256\260/Day3\347\254\224\350\256\260.md" @@ -0,0 +1,237 @@ +## 一、线程间的同步 + +### 1.1 信号量(Semaphore) + +#### 概念 + +作为一种实现多线程共享资源访问控制的同步手段,信号量常用来表示可用资源的数量,在线程同步和资源管理场景中发挥重要作用。它分为两类,即: + + + +* 二值信号量(取值仅为 0 或 1) + +* 计数信号量(取值为非负整数) + +#### 工作机制 + + + +* **创建**:初始化信号量时,要设定一个初始值,这个值通常代表初始的资源数量。 + +* **获取(take)**:当线程需要使用共享资源时,会尝试获取信号量。若信号量当前值大于 0,其值就减 1,线程可继续执行;若值为 0,线程则会进入阻塞状态,直至信号量的值大于 0(即有资源可用)时才会被唤醒。 + +* **释放(release)**:线程使用完共享资源后,会对信号量进行释放,使其值加 1。要是有其他线程在等待该信号量,会有一个线程被唤醒,具体唤醒哪个线程取决于等待策略(如先进先出(FIFO)或者按照优先级)。 + +#### 常见用途 + + + +* 对同时访问共享资源的线程数量加以限制(例如控制同时访问某个硬件的线程数量)。 + +* 实现线程间的同步(例如一个线程完成任务后向另一个线程发出通知)。 + +#### 示例代码 + +打开 RT-Studio,新建工程。在界面左侧的 RT-Thread Settings 中,找到软件包的杂项软件包,选中 samples,再打开 rt-thread 软件包内核实例,接着依次点开 semaphore、mutex 和 event。还能将注释修改为中文,之后按 Ctrl+s 保存文件,编译运行后,就能直观看到信号量的使用方式了。 + +### 1.2 互斥量(Mutex) + +#### 概念 + +互斥量属于一种特殊的二值信号量,主要用于实现共享资源的互斥访问。和普通信号量不同的是,互斥量存在所有权的概念(只有获取到互斥量的线程才有权释放它),且支持优先级继承功能(能够有效避免优先级反转问题)。 + +#### 工作机制 + + + +* **创建**:互斥量在初始状态下处于可用(解锁)状态。 + +* **获取(lock)**:线程会尝试获取互斥量。如果互斥量当前没有被其他线程占用,该线程就能成功获取(互斥量变为锁定状态,该线程成为互斥量的所有者);要是互斥量已被其他线程占用,当前线程就会进入阻塞状态,直到互斥量被释放。 + +* **释放(unlock)**:只有持有互斥量的线程才能进行释放操作。释放后,互斥量回到解锁状态。若有其他线程在等待这个互斥量,其中一个线程会被唤醒并获取到互斥量。 + +#### 特点 + + + +* **所有权**:只有持有互斥量的线程才可以释放它。 + +* **优先级继承**:当高优先级线程等待一个被低优先级线程持有的互斥量时,低优先级线程会暂时提升到高优先级,这一机制能防止因中优先级线程抢占资源而导致高优先级线程长时间阻塞的优先级反转问题。 + +#### 应用场景 + +保护共享资源,保证同一时间只有一个线程能访问该资源。 + +#### 示例代码 + +与信号量的示例代码操作相同。 + +### 1.3 事件集(Event) + +#### 概念 + +事件集是线程间同步的一种机制,允许单个线程等待多个事件的发生,并且可以设定等待多个事件的任意组合形式(如等待任意一个事件发生,或者等待所有事件都发生)。在通常的 32 位系统中,每个事件由一个位来表示,支持 32 个事件。 + +#### 工作机制 + + + +* **创建**:事件集初始化时,所有位都为 0,表示没有任何事件发生。 + +* **发送(send)**:线程可以对事件集中的某些位进行设置,以此表示相应的事件发生。发送事件的操作会唤醒正在等待这些事件的线程。 + +* **接收(recv)**:线程可以等待事件集中的事件。等待时,需要指定以下内容: + + + * 等待的具体事件(通过事件掩码来确定)。 + + * 等待条件(是等待所有指定事件都发生(逻辑与),还是等待任意一个指定事件发生(逻辑或))。 + + * 是否清除事件(当成功接收到事件后,是否自动将事件标志清零)。 + +#### 示例代码 + +与信号量的示例代码操作相同。 + +## 二、线程间的通信 + +### 2.1 邮箱(Mailbox) + +#### 概念 + +邮箱是线程间通信的一种机制,允许线程向另一个线程发送固定大小的消息。在 32 位系统中,消息通常为 4 字节(可能是一个指针或者一个整数值)。邮箱中的每个消息槽只能存放一条消息,当邮箱已满时,发送线程可以选择等待或立即返回。 + +#### 工作机制 + + + +* **初始化**:创建邮箱时,要指定邮箱的容量(可存放的消息数量)和消息大小(在 RT-Thread 中固定为 4 字节)。 + +* **发送消息**: + + + * 若邮箱未满,消息会被复制到邮箱的一个槽中,同时唤醒正在等待接收消息的线程(如果有的话)。 + + * 若邮箱已满,发送线程可以选择挂起等待(阻塞)直到有空闲的消息槽,也可以立即返回错误信息。 + +* **接收消息**: + + + * 若邮箱中有消息,接收线程会取出消息(复制到提供的缓冲区中),并唤醒正在等待发送消息的线程(如果有空间释放的话)。 + + * 若邮箱为空,接收线程可以选择挂起等待(阻塞)直到有消息到来,或者立即返回错误信息。 + +#### 关键特性 + + + +* 消息大小固定(通常为 4 字节,适合传递指针或整数)。 + +* 可以传递指向更大数据块的指针,但要确保接收线程访问数据时,该数据仍然有效(通常需要动态分配内存,并明确释放责任)。 + +* 支持超时机制。 + +#### 典型应用 + + + +* 传递简单的整数值或状态标志。 + +* 传递指针,指向动态分配的数据结构(需注意内存管理)。 + +* 实现轻量级的事件通知。 + +### 2.2 消息队列(Message Queue) + +#### 概念 + +消息队列是邮箱的扩展形式,允许发送长度不固定的消息(但每条消息有最大长度限制)。消息队列内部维护着一个消息缓冲区,每条消息都由消息头和消息内容组成。发送消息时,消息会被复制到队列缓冲区;接收消息时,再从缓冲区中复制出来。 + +#### 工作机制 + + + +* **初始化**:创建消息队列时,需要确定队列中消息的最大数量、每条消息的最大长度,并分配相应的存储空间(通常是一个连续的内存块)。 + +* **发送消息**: + + + * 若队列中有足够的空间存放新消息,消息会被复制到队列的缓冲区中,同时唤醒等待接收消息的线程。 + + * 若队列空间不足,发送线程可以选择挂起等待(阻塞)直到有空间可用,或者立即返回错误信息。 + +* **接收消息**: + + + * 若队列中有消息,接收线程会将消息从队列缓冲区复制到用户提供的缓冲区(缓冲区需要足够大),并唤醒等待发送消息的线程(如果有空间释放的话)。 + + * 若队列为空,接收线程可以选择挂起等待(阻塞)直到有消息到来,或者立即返回错误信息。 + +#### 关键特性 + + + +* 支持可变长度消息(每条消息长度在初始化时设定的最大值范围内)。 + +* 消息在传递过程中会被复制,发送方和接收方操作的是不同的内存副本(避免了共享内存的同步问题)。 + +* 支持超时机制。 + +* 相比邮箱,灵活性更高,但开销也更大。 + +#### 典型应用 + + + +* 传递长度可变的数据(如字符串、结构体等)。 + +* 用于需要传递较大数据块的场景(可通过传递指针,但要注意内存管理)。 + +* 实现多线程之间复杂消息的传递。 + +### 2.3 信号(Signal) + +#### 概念 + +信号是一种异步通信机制,和操作系统中的软中断类似。一个线程可以向另一个线程(或者自身)发送信号,接收线程收到信号后,会中断当前的执行流程,转而去执行预先注册的信号处理函数。处理完成后,线程再回到被中断的地方继续执行。 + +#### 工作机制 + + + +* **安装信号处理函数**:线程通过 rt\_signal\_install () 为特定的信号注册处理函数。 + +* **阻塞 / 解除阻塞信号**:线程可以通过 rt\_signal\_mask () 屏蔽某些信号,或者通过 rt\_signal\_unmask () 解除屏蔽。 + +* **发送信号**:线程通过 rt\_thread\_kill ()(注意:该函数名类似 Unix 的 kill,但仅用于发送信号)向目标线程发送信号。 + +* **信号处理**: + + + * 当目标线程被调度运行时,如果收到信号且该信号未被屏蔽,就会调用相应的处理函数。 + + * 信号处理函数在目标线程的上下文中执行(类似于中断,但属于线程上下文)。 + + * 信号处理函数应尽可能简短,避免调用可能引起阻塞的函数。 + +#### 关键特性 + + + +* **异步通知**:信号可以在任何时候发送给目标线程。 + +* **处理函数执行上下文**:处理函数在目标线程的上下文执行,会中断目标线程的正常执行流程。 + +* **信号类型支持**:支持标准信号(如 SIGINT、SIGUSR1 等),也可以自定义信号。 + +* **信号控制**:线程可以对信号进行屏蔽(暂时不处理)或解除屏蔽。 + +#### 典型应用 + + + +* 中断线程的正常流程,以处理异常情况。 + +* 实现自定义事件通知(如超时提醒、外部事件等)。 + +* 模拟 Unix 风格的信号处理(如 Ctrl+C 中断)。 \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\347\254\224\350\256\260/Day4\347\254\224\350\256\260.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\347\254\224\350\256\260/Day4\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..2386a68cfdb9502f0933a93f94f2b2acc51a7082 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\347\254\224\350\256\260/Day4\347\254\224\350\256\260.md" @@ -0,0 +1,37 @@ +# RT-Thread 的 IO 设备模型与设备驱动框架 + +## 一、IO 设备模型的核心构成 + +RT-Thread 的 IO 设备模型作为内核关键组件,其核心价值在于构建**统一的硬件访问桥梁**。通过抽象化处理,让上层应用能以标准化方式操作各类硬件,彻底屏蔽底层硬件的技术细节差异。 + +### 1. 设备的抽象形态 + +在该模型中,所有硬件设备均被转化为**内核可管理的对象**。每个设备拥有唯一标识名称,且通过专属驱动程序与实际硬件建立连接,实现软件层面的统一管控。 + +### 2. 驱动程序的角色 + +设备驱动承担着**硬件操作的翻译工作**,它将读、写、控制等标准化设备操作,精准转换为适配特定硬件的指令序列,是连接抽象设备与物理硬件的关键纽带。 + +### 3. 设备的分类体系 + +依据功能特性,设备被划分为若干类别,例如字符设备、块设备、网络设备及传感器设备等。**设备类型直接决定了其支持的操作集合**,为应用访问提供清晰指引。 + +### 4. 标准化操作接口 + +驱动程序需实现一组预设的操作函数,包括初始化、打开、读取、写入、控制等,这些接口构成了应用与设备交互的标准通道,确保操作方式的一致性。 + +## 二、设备驱动框架的核心特性 + +设备驱动框架是操作系统内核中负责**硬件交互标准化的关键软件层**,其设计目标是简化应用开发与驱动实现,同时实现硬件资源的高效管理。 + +### 1. 应用层的访问方式 + +框架为应用程序提供了统一的标准 API,如 read () 和 write () 等函数,使得应用开发者无需了解硬件细节,即可完成对各类设备的操作,极大降低了开发难度。 + +### 2. 驱动开发的支持 + +通过提炼通用逻辑并纳入框架,驱动开发者可将精力集中于处理硬件的特有差异,大幅减少重复开发工作,提升驱动程序的开发效率和质量。 + +### 3. 硬件资源的管理 + +框架承担着操作系统对硬件资源的统一管理职责,包括设备的注册、注销、状态监控等,确保硬件资源得到合理分配和高效利用,保障系统的稳定运行。 \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\347\254\224\350\256\260/Day5\347\254\224\350\256\260.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\347\254\224\350\256\260/Day5\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..3baab2c8511f0fafd4d88008e05fffdfb8b934e3 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\350\202\226\346\231\257\345\263\260/\347\254\224\350\256\260/Day5\347\254\224\350\256\260.md" @@ -0,0 +1,84 @@ +# RT-Thread 工程配置与软件包管理指南 + +## 1. 工程配置基础 + +### 配置文件体系 +- **.config 文件**:通过 env 工具的 menuconfig 配置后生成,记录所有开启的配置选项 +- **rtconfig.h 文件**:由 .config 文件通过 scons 生成,真正作用于源码编译 + - 不要手动修改,工程刷新时会自动更新(类似 HAL CUBE IDE 开发模式) +- 配置文件转换流程:`Kconfig(.config) ──scons──▶ rtconfig.h ──编译器──▶ 编译选项` + +### 配置操作流程 +1. 使用 menuconfig 进行配置 +2. 配置完成后生成 .config 文件 +3. 执行 `pkgs --update` 更新新增的软件包或配置 + +## 2. Kconfig 配置系统 + +### Kconfig 作用 +- 配合 scons 编译器完成 RT-Thread 内核项目裁剪 +- menuconfig 中显示的内容由分布在各个子目录的 Kconfig 文件定义 + +### Kconfig 语法示例 +``` +config BSP_USING_GPIO + bool "Enable GPIO" + select RT_USING_PIN + default n + help + Enable or disable GPIO support. +``` + +### 语法说明 +- **config**:配置选项的开始,后跟选项名称(如 BSP_USING_GPIO) +- **类型定义**:每个配置选项必须指定类型 + - bool:布尔类型(y/n) + - tristate:三态类型 + - string:字符串类型 + - hex:十六进制类型 + - int:整型 +- **select**:反向依赖关系,当前选项被选中时,所指定的选项也会被选中 +- **default**:配置选项的默认值 +- **help**:帮助信息 + +### 配置结果 +选中选项后,会在 rtconfig.h 中生成对应的宏定义: +```c +#define RT_USING_PIN +#define BSP_USING_GPIO +``` + +### Kconfig 文件组织 +- `osource`:引用上级目录的 Kconfig 文件 +- `source`:引用当前目录的子目录中的 Kconfig 文件 + +## 3. 软件包管理 + +### 软件包定义 +- 运行于 RT-Thread 物联网操作系统平台上的软件组件 +- 由软件包描述信息、源代码或库文件组成 +- 应避免耦合产品业务逻辑代码,提高通用性 + +### 软件包更新 +- **自动更新**:`pkgs --update` 更新新增的软件包或配置 +- **索引更新**:当 env 下的 menuconfig 和官网软件包不匹配时,执行 `pkgs --upgrade` 更新 env 软件包索引 +- **手动更新**:当 Env 工具无法自动获取最新软件包时 + ```bash + git pull origin master # 更新官方软件包仓库 + ``` + +### 软件包更新问题解决 +- 若 `pkgs --update` 卡住,可能是网络问题,建议使用代理(挂梯子)解决 + +## 4. LLM-chat 组件配置要点 + +### 前期准备 +- 在阿里云模型市场(https://bailian.console.aliyun.com)完成账号注册 +- 获取 API 密钥 + +### 关键配置 +``` +CONFIG_LLM_MAX_FRAGMENT_LENGTH=6144 # 调整最大分片长度(字节) +CONFIG_USING_MBEDTLS=y # 启用 MbedTLS 加密库 +CONFIG_USING_EMAC=y # 启用以太网 MAC 驱动 +``` \ No newline at end of file