diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day2main.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day2main.c" new file mode 100644 index 0000000000000000000000000000000000000000..94fc0a78205c740d5cb2dcf1ee0c9228996f1e25 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day2main.c" @@ -0,0 +1,58 @@ +// 这里是第2天作业的代码 + +#include +#include +#include + +/** + * 定义三个函数用于创建线程 + * usr_thread_high_priority:高优先级线程用于实验抢占式调度 + * + * usr_thread_low_priority_1 + * usr_thread_low_priority_2:低优先级线程且优先级相同,用于实验时间片轮转的调度 + * + */ +void usr_thread_high_priority(void){ + while(1){ + for(int i = 0;i<10;i++){ + rt_kprintf("run in usr high priority\r\n"); + } + rt_thread_delay(100); // 主动释放CPU给时间让另外两个线程执行 + } +} + +void usr_thread_low_priority_1(void){ + while(1){ + rt_kprintf("run in usr low priority 1\r\n"); + } +} + +void usr_thread_low_priority_2(void){ + while(1){ + rt_kprintf("run in usr low priority 2\r\n"); + } +} + +rt_thread_t tid1 = RT_NULL; +rt_thread_t tid2 = RT_NULL; +rt_thread_t tid3 = RT_NULL; + +int main(void) { + // 创建并启动三个线程 + tid1 = rt_thread_create("usr1", usr_thread_high_priority, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY + 1, 5); + if(tid1 != RT_NULL){ + rt_thread_startup(tid1); + } + + tid2 = rt_thread_create("usr2", usr_thread_low_priority_1, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY + 3, 5); + if(tid2 != RT_NULL){ + rt_thread_startup(tid2); + } + + tid3 = rt_thread_create("usr3", usr_thread_low_priority_2, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY + 3, 10); + if(tid2 != RT_NULL){ + rt_thread_startup(tid3); + } + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3Event.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3Event.c" new file mode 100644 index 0000000000000000000000000000000000000000..a0fc348b698404aff8149f6b9df39167cd482201 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3Event.c" @@ -0,0 +1,67 @@ +#include +#include +#include + +#define EVENT_FLAG1 (1 << 1) +#define EVENT_FLAG2 (1 << 2) + +static rt_event_t event = RT_NULL; + +void usr_thread1(void){ + rt_kprintf("run usr1\n"); + rt_uint32_t rcv; + rt_kprintf("waiting for event1 or event2"); + //以或的形式接收event1, event2 + if(rt_event_recv(event, (EVENT_FLAG1 | EVENT_FLAG2), + RT_EVENT_FLAG_OR, RT_WAITING_FOREVER, &rcv) == RT_EOK){ + rt_kprintf("usr1: OR receive event0x%x\n", rcv); + } + + rt_kprintf("waiting for event1 and event2\n"); + //以且的形式接收event1, event2 + if(rt_event_recv(event, (EVENT_FLAG1 | EVENT_FLAG2), + RT_EVENT_FLAG_AND, RT_WAITING_FOREVER, &rcv) == RT_EOK){ + rt_kprintf("usr1: AND receive event0x%x\n", rcv); + } + rt_event_delete(event); + rt_kprintf("end\n"); +} + +void usr_thread2(void){ + rt_kprintf("run usr2\n"); + rt_kprintf("event1 sent\n"); + rt_event_send(event, EVENT_FLAG1); +} + +void usr_thread3(void){ + rt_kprintf("run usr3\n"); + rt_kprintf("event2 sent\n"); + rt_event_send(event, EVENT_FLAG2); +} + +rt_thread_t tid1 = RT_NULL; +rt_thread_t tid2 = RT_NULL; +rt_thread_t tid3 = RT_NULL; + +int main(void) { + // 创建事件集 + event = rt_event_create("event1", RT_IPC_FLAG_PRIO); + + // 创建三个线程 + tid1 = rt_thread_create("usr1", usr_thread1, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY - 3, 5); + if(tid1 != RT_NULL){ + rt_thread_startup(tid1); + } + + tid2 = rt_thread_create("usr2", usr_thread2, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY - 2, 5); + if(tid2 != RT_NULL){ + rt_thread_startup(tid2); + } + + tid3 = rt_thread_create("usr3", usr_thread3, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY - 1, 5); + if(tid3 !=RT_NULL){ + rt_thread_startup(tid3); + } + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3Mailbox.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3Mailbox.c" new file mode 100644 index 0000000000000000000000000000000000000000..d249302786c05c74745b435f3b305ebfd2ea692a --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3Mailbox.c" @@ -0,0 +1,46 @@ +#include +#include +#include + +static rt_mailbox_t mb = RT_NULL; + +void usr_thread1(void){ + rt_kprintf("run usr1\n"); + int recv = 0; + while(recv<9){ + if(rt_mb_recv(mb, (rt_ubase_t*)&recv, RT_WAITING_FOREVER) == RT_EOK){ + rt_kprintf("usr1: get a mail from mailbox, content:%d\n",recv); + } + } + rt_mb_delete(mb); +} + +void usr_thread2(void){ + rt_kprintf("run usr2\n"); + for(int i = 0;i<10;i++){ + rt_kprintf("usr2 send a mail, content:%d\n",i); + rt_mb_send(mb, (rt_uint32_t)i); + rt_thread_mdelay(50); + } +} + +rt_thread_t tid1 = RT_NULL; +rt_thread_t tid2 = RT_NULL; + +int main(void) { + //创建邮箱 + mb = rt_mb_create("mb1", 10, RT_IPC_FLAG_PRIO); + + //启动两个线程 + tid1 = rt_thread_create("usr1", usr_thread1, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY - 2, 5); + if(tid1 != RT_NULL){ + rt_thread_startup(tid1); + } + + tid2 = rt_thread_create("usr2", usr_thread2, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY - 2, 5); + if(tid2 != RT_NULL){ + rt_thread_startup(tid2); + } + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3MessageQueue.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3MessageQueue.c" new file mode 100644 index 0000000000000000000000000000000000000000..851919b10b0bcb510d7ddd1499044dae24806a4d --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3MessageQueue.c" @@ -0,0 +1,54 @@ +#include +#include +#include + +static rt_mq_t mq = RT_NULL; + +void usr_thread1(void){ + rt_kprintf("run usr1\n"); + int recv = 0; + //接收10个数字 + while(recv<9){ + if(rt_mq_recv(mq, &recv, sizeof(recv), RT_WAITING_FOREVER) == RT_EOK){ + rt_kprintf("usr1: get a msg from msgq, content:%d\n",recv); + } + rt_thread_mdelay(50); + } + rt_mq_delete(mq); +} + +void usr_thread2(void){ + rt_kprintf("run usr2\n"); + //发送10个数字,5为紧急消息。 + for(int i = 0;i<10;i++){ + if(i != 5){ + rt_kprintf("usr2 send a msg, content:%d\n",i); + rt_mq_send(mq, &i, sizeof(i)); + }else{ + rt_kprintf("usr2 send an urgent msg, content:%d\n",i); + rt_mq_urgent(mq, &i, sizeof(i)); + } + rt_thread_mdelay(9); + } +} + +rt_thread_t tid1 = RT_NULL; +rt_thread_t tid2 = RT_NULL; + +int main(void) { + // 创建一个消息队列 + mq = rt_mq_create("msgq1", sizeof(int), 10, RT_IPC_FLAG_PRIO); + + //创建两个线程 + tid1 = rt_thread_create("usr1", usr_thread1, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY - 2, 5); + if(tid1 != RT_NULL){ + rt_thread_startup(tid1); + } + + tid2 = rt_thread_create("usr2", usr_thread2, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY - 2, 5); + if(tid2 != RT_NULL){ + rt_thread_startup(tid2); + } + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3Mutex.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3Mutex.c" new file mode 100644 index 0000000000000000000000000000000000000000..75c9110c8f34b9d421cd60eaca9323241ec50c72 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3Mutex.c" @@ -0,0 +1,61 @@ +#include +#include +#include + +static rt_mutex_t mutex = RT_NULL; +static rt_uint8_t count = 0, cycle = 10; + +void usr_thread1(void){ + rt_kprintf("run usr1\n"); + while(cycle > 0){ + count = 0; + //申请互斥量 + rt_mutex_take(mutex, RT_WAITING_FOREVER); + rt_kprintf("usr1 message1\n"); + ++count; + rt_thread_mdelay(50);//让出CPU + rt_kprintf("usr1 message2\n"); + ++count; + //释放互斥量 + rt_mutex_release(mutex); + + --cycle; + } +} + +void usr_thread2(void){ + rt_kprintf("run usr2\n"); + while(1){ + //申请互斥量 + rt_mutex_take(mutex, RT_WAITING_FOREVER); + if(count&0x1){ + rt_kprintf("single message detect\n"); + }else{ + rt_kprintf("double message print\n"); + } + //释放互斥量 + rt_mutex_release(mutex); + if(cycle == 0)break;//打印完毕退出 + } + rt_mutex_delete(mutex); +} + +rt_thread_t tid1 = RT_NULL; +rt_thread_t tid2 = RT_NULL; + +int main(void) { + //创建互斥量 + mutex = rt_mutex_create("mutex1", RT_IPC_FLAG_PRIO); + + tid1 = rt_thread_create("usr1", usr_thread1, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY - 1, 5); + if(tid1 != RT_NULL){ + rt_thread_startup(tid1); + } + + tid2 = rt_thread_create("usr2", usr_thread2, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY - 1, 5); + if(tid2 != RT_NULL){ + rt_thread_startup(tid2); + } + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3Semaphore.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3Semaphore.c" new file mode 100644 index 0000000000000000000000000000000000000000..cdb44b327666b639f4ede00ec09735a623da7ec1 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3Semaphore.c" @@ -0,0 +1,47 @@ +#include +#include +#include + +static rt_sem_t sem1 = RT_NULL; + +void usr_thread1(void){ + rt_kprintf("run usr1\n"); + // 尝试获取信号量 + if(rt_sem_take(sem1, RT_WAITING_FOREVER) != RT_EOK){ + rt_kprintf("usr1 take sem faild\n"); + }else{ + rt_kprintf("usr1 run later\n"); + } + rt_sem_delete(sem1); +} + +void usr_thread2(void){ + rt_kprintf("run usr2\n"); + rt_kprintf("usr2 delay 30ms\n"); + rt_thread_mdelay(30); + rt_kprintf("usr2 run first then release semaphore\n"); + // 释放信号量 + rt_sem_release(sem1); +} + +rt_thread_t tid1 = RT_NULL; +rt_thread_t tid2 = RT_NULL; + +int main(void) { + //创建同步信号量 + sem1 = rt_sem_create("sem1", 0, RT_IPC_FLAG_PRIO); + + //创建线程1,优先级较高,先执行 + tid1 = rt_thread_create("usr1", usr_thread1, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY - 3, 5); + if(tid1 != RT_NULL){ + rt_thread_startup(tid1); + } + + // 创建线程2,优先级较低,后执行 + tid2 = rt_thread_create("usr2", usr_thread2, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY - 1, 5); + if(tid2 != RT_NULL){ + rt_thread_startup(tid2); + } + + return RT_EOK; +} \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3Signal.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3Signal.c" new file mode 100644 index 0000000000000000000000000000000000000000..48f7b4a23eac9a78583cdd1be0b7401004d601a9 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day3Signal.c" @@ -0,0 +1,33 @@ +#include +#include +#include + +//handle回调函数 +void usr_thread1_handler(int signal){ + rt_kprintf("handle signal %d\n", signal); +} + +void usr_thread1(void){ + //安装并响应信号 + rt_signal_install(SIGUSR1, usr_thread1_handler); + rt_signal_unmask(SIGUSR1); + for(int i = 0;i < 10;i++){ + rt_kprintf("waiting for kill %d\n", i); + rt_thread_mdelay(50); + } +} + +rt_thread_t tid1 = RT_NULL; + +int main(void){ + //创建线程 + tid1 = rt_thread_create("usr1", usr_thread1, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY - 2, 5); + if(tid1 != RT_NULL){ + rt_thread_startup(tid1); + } + rt_thread_mdelay(300); + //向线程发送信号 + rt_thread_kill(tid1, SIGUSR1); + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day4drv_my_vir.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day4drv_my_vir.c" new file mode 100644 index 0000000000000000000000000000000000000000..9ff8f8969a7917646b5cf44d2d0095e2c37a9437 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day4drv_my_vir.c" @@ -0,0 +1,48 @@ +#include + +#if defined(RT_USING_MY_VIR) + +struct my_vir_test vir; + + +void setstatus(struct rt_device *device, rt_uint16_t status){ + vir.status = status; + rt_kprintf("the status is set to 0x%x\n", status); +} + +void getstatus(struct rt_device *device, rt_uint16_t *status){ + *status = vir.val; +} + +void writenumber(struct rt_device *device, rt_uint32_t number){ + if(vir.status == MY_VIR_OPEN){ + vir.val = number; + return; + } + rt_kprintf("my vir is in close status\n"); + +} + +void readnumber(struct rt_device *device, rt_uint32_t *number){ + if(vir.status == MY_VIR_OPEN){ + *number = vir.val; + return; + } + rt_kprintf("my vir is in close status\n"); +} + + +struct my_vir_ops ops = { + setstatus, + getstatus, + writenumber, + readnumber +}; + +static int my_vir_init(void){ + vir.status = MY_VIR_CLOSE; + rt_hw_my_vir_register(&vir.parent, "my_vir", &ops, (void*)&vir.status); +} +//自动初始化 +INIT_APP_EXPORT(my_vir_init); +#endif diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day4drv_my_vir.h" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day4drv_my_vir.h" new file mode 100644 index 0000000000000000000000000000000000000000..8be0f1b2a99794db6903cd9d9b5161806b51bb04 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day4drv_my_vir.h" @@ -0,0 +1,15 @@ +#ifndef __DRV_MY_VIR_H__ +#define __DRV_MY_VIR_H__ + +#include +#include + +struct my_vir_test { + struct my_vir_device parent;//继承设备驱动框架层 + rt_uint32_t val;//设备保存的值 + rt_uint16_t status;//保存设备的状态 +}; + + + +#endif diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day4main.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day4main.c" new file mode 100644 index 0000000000000000000000000000000000000000..6967673b47540ad1e68a778ae5a1f34e05127df2 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day4main.c" @@ -0,0 +1,38 @@ +#include +#include +#include + +int main(void) { + //通过查找获得设备句柄 + rt_device_t my_vir = rt_device_find("my_vir"); + if(my_vir == RT_NULL){ + rt_kprintf("find my_vir err\n"); + } + + rt_uint32_t buffer = 100; + rt_uint32_t ret = 0; + + //使用IO设备管理层操作。 + //一定要先打开设备,否则无法进行后续的操作,设备管理层会直接拦下接下来的读写操作。 + rt_device_open(my_vir, MY_VIR_OPEN); + //在未设置打开模式的情况下应该会打印错误信息 + rt_device_write(my_vir, 0, &buffer, sizeof(buffer)); + rt_device_read(my_vir, 0, &ret, sizeof(ret)); + rt_kprintf("ret: %d\n", ret); + + rt_uint16_t arg = MY_VIR_OPEN; + //打开设备 + rt_device_control(my_vir, 0, &arg); + rt_device_write(my_vir, 0, &buffer, sizeof(buffer)); + rt_device_read(my_vir, 0, &ret, sizeof(ret)); + rt_kprintf("ret: %d\n", ret); + + buffer = 200; + //使用设备驱动框架层API操作 + my_vir_write((my_vir_device_t)my_vir, buffer); + my_vir_read((my_vir_device_t)my_vir, &ret); + rt_kprintf("ret: %d\n", ret); + + rt_device_close(my_vir); + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day4my_vir.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day4my_vir.c" new file mode 100644 index 0000000000000000000000000000000000000000..9c6dc7f760b3d36b29fde0dee2459e4ed4fd8f4b --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day4my_vir.c" @@ -0,0 +1,102 @@ +#include +#include + +/** + * 这里是my_vir设备的驱动框架层实现,它是一个完全虚拟的设备,不对接实际驱动。 + */ + +#if defined(RT_USING_MY_VIR) + + +rt_err_t _my_vir_init(rt_device_t dev){ + rt_kprintf("init my vir\n"); + return RT_EOK; +} + +rt_err_t _my_vir_open(rt_device_t dev, rt_uint16_t oflag){ + rt_kprintf("open my vir\n",oflag); + return RT_EOK; +} + +rt_err_t _my_vir_close(rt_device_t dev){ + my_vir_device_t device = (my_vir_device_t)dev; + if(device->ops && device->ops->setstatus){ + rt_kprintf("close my vir\n"); + device->ops->setstatus(dev, MY_VIR_CLOSE); + return RT_EOK; + } + return -RT_ERROR; + +} +rt_size_t _my_vir_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size){ + my_vir_device_t device = (my_vir_device_t)dev; + if(device->ops->readnumber){ + device->ops->readnumber(dev, (rt_uint32_t*)buffer); + return RT_EOK; + } + return -RT_ERROR; +} +rt_size_t _my_vir_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size){ + my_vir_device_t device = (my_vir_device_t)dev; + if(device->ops->writenumber){ + device->ops->writenumber(dev, *(rt_uint32_t*)buffer); + return RT_EOK; + } + return -RT_ERROR; +} +rt_err_t _my_vir_control(rt_device_t dev, int cmd, void *args){ + rt_kprintf("control my vir\n"); + my_vir_device_t device = (my_vir_device_t)dev; + if(device->ops && device->ops->setstatus){ + device->ops->setstatus(dev, *(rt_uint16_t*)args); + return RT_EOK; + } + return -RT_ERROR; +} + + + +rt_err_t rt_hw_my_vir_register(my_vir_device_t device, const char *name, const struct my_vir_ops *ops, const void *user_data) +{ + RT_ASSERT(ops != RT_NULL); + rt_err_t result; + + device->ops = ops; + //向IO设备管理层注册函数 + device->parent.init = _my_vir_init; + device->parent.open = _my_vir_open; + device->parent.close = _my_vir_close; + device->parent.read = _my_vir_read; + device->parent.write = _my_vir_write; + device->parent.control = _my_vir_control; + + result = rt_device_register(&device->parent, name, RT_DEVICE_FLAG_RDWR); + + return result; +} + +/** + * 此处实现的是驱动框架层的特殊API,开发者可以不通过IO设备管理层直接使用以下API + */ + +rt_err_t my_vir_read(my_vir_device_t dev, rt_uint32_t * val){ + RT_ASSERT(dev != RT_NULL && dev->ops != RT_NULL); + if(dev->ops->readnumber){ + rt_device_t device = (rt_device_t)dev; + dev->ops->readnumber(device, val); + return RT_EOK; + } + return -RT_ERROR; +} + +rt_err_t my_vir_write(my_vir_device_t dev, rt_uint32_t val){ + RT_ASSERT(dev != RT_NULL && dev->ops != RT_NULL); + if(dev->ops->writenumber){ + rt_device_t device = (rt_device_t)dev; + dev->ops->writenumber(device, val); + return RT_EOK; + } + return -RT_ERROR; +} + +#endif diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day4my_vir.h" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day4my_vir.h" new file mode 100644 index 0000000000000000000000000000000000000000..8c5eb1e85c67a80fc712451c36f52f611ca01c2b --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/Day4my_vir.h" @@ -0,0 +1,36 @@ +#ifndef __MY_VIR_H__ +#define __MY_VIR_H__ +#include + + +/** + * 这里是实现的虚拟的设备驱动框架层,不会实际驱动任何硬件 + * .c实现在misc杂项中 + */ +#define MY_VIR_CLOSE 0x00 +#define MY_VIR_OPEN 0x01 + +//定义设备驱动层的抽象接口 +struct my_vir_ops{ + void (*setstatus)(struct rt_device *device, rt_uint16_t status); + void (*getstatus)(struct rt_device *device, rt_uint16_t *status); + void (*writenumber)(struct rt_device *device, rt_uint32_t number); + void (*readnumber)(struct rt_device *device, rt_uint32_t *number); +}; + + + +struct my_vir_device +{ + struct rt_device parent;//继承父类 + const struct my_vir_ops *ops;//驱动层的接口 +}; +typedef struct my_vir_device *my_vir_device_t; + +rt_err_t rt_hw_my_vir_register(my_vir_device_t device, const char *name, const struct my_vir_ops *ops, const void *user_data); +//下面是设备驱动框架层的特殊API,开发者可以直接使用而不通过IO设备管理层访问 +rt_err_t my_vir_read(my_vir_device_t dev, rt_uint32_t * val); + +rt_err_t my_vir_write(my_vir_device_t dev, rt_uint32_t val); + +#endif diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day2/polling.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day2/polling.png" new file mode 100644 index 0000000000000000000000000000000000000000..591df48a63a051d3aa645e0f39ac20a73d28568a Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day2/polling.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day2/preemptive.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day2/preemptive.png" new file mode 100644 index 0000000000000000000000000000000000000000..2c8f03639e21af81dc8b7f3f4b05e91aad6dc676 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day2/preemptive.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/event.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/event.png" new file mode 100644 index 0000000000000000000000000000000000000000..3cf60bd5eb6a25b0b7915f1399ea7193ac7f00e0 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/event.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/mailbox.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/mailbox.png" new file mode 100644 index 0000000000000000000000000000000000000000..01dd2c46981ab341dee9cb94c74bd0f8fabdfadf Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/mailbox.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/msgq.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/msgq.png" new file mode 100644 index 0000000000000000000000000000000000000000..6de715664e4870a69aa7d8a60815d242d18cc9b5 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/msgq.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/mutex.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/mutex.png" new file mode 100644 index 0000000000000000000000000000000000000000..514f9594752d9bb5f132b9235a88d5889f9f3a24 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/mutex.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/semaphore.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/semaphore.png" new file mode 100644 index 0000000000000000000000000000000000000000..c795f4f8f0b2a5009efd7efdcbf89730fbaaf9a5 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/semaphore.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/signal.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/signal.png" new file mode 100644 index 0000000000000000000000000000000000000000..cdc827e9dbc047a41b9939a2bccdcf3a35e99329 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day3/signal.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day4/menuconfig.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day4/menuconfig.png" new file mode 100644 index 0000000000000000000000000000000000000000..cad465b7a909b84e98588f0bc368b180a2af3920 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day4/menuconfig.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day4/result.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day4/result.png" new file mode 100644 index 0000000000000000000000000000000000000000..bf270fdefa28ae83dd65f1e3291afc414fecb5f3 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/figures/day4/result.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/\347\254\2541\345\244\251\344\275\234\344\270\232.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/\347\254\2541\345\244\251\344\275\234\344\270\232.md" new file mode 100644 index 0000000000000000000000000000000000000000..3b220eb0a241a33aaa831a4217b79291ad9989c9 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/\347\254\2541\345\244\251\344\275\234\344\270\232.md" @@ -0,0 +1,5 @@ +## 针对qemu平台的项目编译 +已经写在[第1天笔记](../笔记/第1天笔记.md#尝试针对qemu平台编译)中了,此处不再赘述。 + +## 整理并提交笔记 +详见夏令营仓库的[PR列表](https://gitee.com/rtthread/rsoc-rtt/pulls)及[夏令营作业收集表](https://docs.qq.com/sheet/DSkFwaW9GZVpYY0VI?tab=BB08J2) \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/\347\254\2542\345\244\251\344\275\234\344\270\232.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/\347\254\2542\345\244\251\344\275\234\344\270\232.md" new file mode 100644 index 0000000000000000000000000000000000000000..546bba897fa0828f599930b6ded5be816529a995 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/\347\254\2542\345\244\251\344\275\234\344\270\232.md" @@ -0,0 +1,38 @@ +## 线程设计 +一个高优先级线程用于实验抢占式调度 + +```c +void usr_thread_high_priority(void){ + while(1){ + for(int i = 0;i<10;i++){ + rt_kprintf("run in usr high priority\r\n"); + } + rt_thread_delay(100); // 主动释放CPU给时间让另外两个线程执行 + } +} +``` + +两个低优先级的线程用于实验时间片轮转调度 + +```c +void usr_thread_low_priority_1(void){ + while(1){ + rt_kprintf("run in usr low priority 1\r\n"); + } +} + +void usr_thread_low_priority_2(void){ + while(1){ + rt_kprintf("run in usr low priority 2\r\n"); + } +} +``` + +详细实现见[Day2main.c](Day2main.c) + +## 实验结果 +抢占式 +![preemptive](figures/day2/preemptive.png) + +时间片轮询 +![polling](figures/day2/polling.png) \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/\347\254\2543\345\244\251\344\275\234\344\270\232.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/\347\254\2543\345\244\251\344\275\234\344\270\232.md" new file mode 100644 index 0000000000000000000000000000000000000000..1b1f3696a833c28c0d67b1db6dbcf657fd9a4c53 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/\347\254\2543\345\244\251\344\275\234\344\270\232.md" @@ -0,0 +1,219 @@ +## 信号量semaphore +### 线程设计 +设计两个线程,让低优先级的线程释放信号量,高优先级的线程获得信号量,从而保证低优先级的线程先执行。 +```c +void usr_thread1(void){ + rt_kprintf("run usr1\n"); + // 尝试获取信号量 + if(rt_sem_take(sem1, RT_WAITING_FOREVER) != RT_EOK){ + rt_kprintf("usr1 take sem faild\n"); + }else{ + rt_kprintf("usr1 run later\n"); + } + rt_sem_delete(sem1); +} + +void usr_thread2(void){ + rt_kprintf("run usr2\n"); + rt_kprintf("usr2 delay 30ms\n"); + rt_thread_mdelay(30); + rt_kprintf("usr2 run first then release semaphore\n"); + // 释放信号量 + rt_sem_release(sem1); +} +``` +详细代码查看[Day3Semaphore](Day3Semaphore.c) +### 运行结果 +![信号量](figures/day3/semaphore.png) + +## 互斥量mutex +### 线程设计 +设计两个线程,相同优先级,一个线程会依次打印两条信息,但打印之间会delay,使用互斥量防止打印一次时被检测。线程2检测是否打印了单条信息。 +```c +void usr_thread1(void){ + rt_kprintf("run usr1\n"); + while(cycle > 0){ + count = 0; + //申请互斥量 + rt_mutex_take(mutex, RT_WAITING_FOREVER); + rt_kprintf("usr1 message1\n"); + ++count; + rt_thread_mdelay(50);//让出CPU + rt_kprintf("usr1 message2\n"); + ++count; + //释放互斥量 + rt_mutex_release(mutex); + + --cycle; + } +} + +void usr_thread2(void){ + rt_kprintf("run usr2\n"); + while(1){ + //申请互斥量 + rt_mutex_take(mutex, RT_WAITING_FOREVER); + if(count&0x1){ + rt_kprintf("single message detect\n"); + }else{ + rt_kprintf("double message print\n"); + } + //释放互斥量 + rt_mutex_release(mutex); + if(cycle == 0)break;//打印完毕退出 + } + rt_mutex_delete(mutex); +} +``` +详细代码查看[Day3Mutex](Day3Mutex.c) + +### 运行结果 +![互斥量](figures/day3/mutex.png) + +## 事件集event +### 线程设计 +设计三个线程,两个低优先级的线程分别发出两个事件,一个高优先级的线程接收事件。 +```c +void usr_thread1(void){ + rt_kprintf("run usr1\n"); + rt_uint32_t rcv; + rt_kprintf("waiting for event1 or event2"); + //以或的形式接收event1, event2 + if(rt_event_recv(event, (EVENT_FLAG1 | EVENT_FLAG2), + RT_EVENT_FLAG_OR, RT_WAITING_FOREVER, &rcv) == RT_EOK){ + rt_kprintf("usr1: OR receive event0x%x\n", rcv); + } + + rt_kprintf("waiting for event1 and event2\n"); + //以且的形式接收event1, event2 + if(rt_event_recv(event, (EVENT_FLAG1 | EVENT_FLAG2), + RT_EVENT_FLAG_AND, RT_WAITING_FOREVER, &rcv) == RT_EOK){ + rt_kprintf("usr1: AND receive event0x%x\n", rcv); + } + rt_event_delete(event); + rt_kprintf("end\n"); +} + +void usr_thread2(void){ + rt_kprintf("run usr2\n"); + rt_kprintf("event1 sent\n"); + rt_event_send(event, EVENT_FLAG1); +} + +void usr_thread3(void){ + rt_kprintf("run usr3\n"); + rt_kprintf("event2 sent\n"); + rt_event_send(event, EVENT_FLAG2); +} +``` +详细代码查看[Day3Event](Day3Event.c) + +### 运行结果 +![事件集](figures/day3/event.png) + +## 邮箱mailbox +### 线程设计 +设计两个线程,一个发送数字,一个接收数字 +```c +void usr_thread1(void){ + rt_kprintf("run usr1\n"); + int recv = 0; + //循环接收数字 + while(recv<9){ + if(rt_mb_recv(mb, (rt_ubase_t*)&recv, RT_WAITING_FOREVER) == RT_EOK){ + rt_kprintf("usr1: get a mail from mailbox, content:%d\n",recv); + } + } + rt_mb_delete(mb); +} + +void usr_thread2(void){ + rt_kprintf("run usr2\n"); + //发送数字1-9 + for(int i = 0;i<10;i++){ + rt_kprintf("usr2 send a mail, content:%d\n",i); + rt_mb_send(mb, (rt_uint32_t)i); + rt_thread_mdelay(50); + } +} +``` +详细代码查看[Day3Mailbox](Day3Mailbox.c) + +### 运行结果 +![邮箱](figures/day3/mailbox.png) + +## 消息队列message_queue +### 线程设计 +设计两个线程,一个线程发送数字,同时夹杂一个紧急信息,另一个线程接收数字。 +```c +void usr_thread1(void){ + rt_kprintf("run usr1\n"); + int recv = 0; + //接收10个数字 + while(recv<9){ + if(rt_mq_recv(mq, &recv, sizeof(recv), RT_WAITING_FOREVER) == RT_EOK){ + rt_kprintf("usr1: get a msg from msgq, content:%d\n",recv); + } + rt_thread_mdelay(50); + } + rt_mq_delete(mq); +} + +void usr_thread2(void){ + rt_kprintf("run usr2\n"); + //发送10个数字,5为紧急消息。 + for(int i = 0;i<10;i++){ + if(i != 5){ + rt_kprintf("usr2 send a msg, content:%d\n",i); + rt_mq_send(mq, &i, sizeof(i)); + }else{ + rt_kprintf("usr2 send an urgent msg, content:%d\n",i); + rt_mq_urgent(mq, &i, sizeof(i)); + } + rt_thread_mdelay(9); + } +} +``` +详细代码查看[Day3MessagQueue](Day3MessageQueue.c) + +### 运行结果 +![消息队列](figures/day3/msgq.png) + +## 信号signal +### 线程设计 +设计一个线程,main函数对其发送信号,由该线程处理 +```c +//handle回调函数 +void usr_thread1_handler(int signal){ + rt_kprintf("handle signal %d\n", signal); +} + +void usr_thread1(void){ + //安装并响应信号 + rt_signal_install(SIGUSR1, usr_thread1_handler); + rt_signal_unmask(SIGUSR1); + for(int i = 0;i < 10;i++){ + rt_kprintf("waiting for kill %d\n", i); + rt_thread_mdelay(50); + } +} + +rt_thread_t tid1 = RT_NULL; + +int main(void){ + //创建线程 + tid1 = rt_thread_create("usr1", usr_thread1, RT_NULL, 1024, RT_MAIN_THREAD_PRIORITY - 2, 5); + if(tid1 != RT_NULL){ + rt_thread_startup(tid1); + } + rt_thread_mdelay(300); + //向线程发送信号 + rt_thread_kill(tid1, SIGUSR1); + + return RT_EOK; +} +``` +详细代码查看[Day3Signal](Day3Signal.c) + +### 运行结果 +![消息队列](figures/day3/signal.png) diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/\347\254\2544\345\244\251\344\275\234\344\270\232.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/\347\254\2544\345\244\251\344\275\234\344\270\232.md" new file mode 100644 index 0000000000000000000000000000000000000000..9afb7a0fe939c37a2dade1a66bbd104d21628409 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\344\275\234\344\270\232/\347\254\2544\345\244\251\344\275\234\344\270\232.md" @@ -0,0 +1,226 @@ +## 虚拟设备的驱动框架相关设计与实现 +### 版本环境与总体设计 +使用RT-Thread Studio基于RT-Thread4.1.0版本实现,与最新版的目录结构有些微不同 + +设计实现一个虚拟的设备,在驱动层保留一个变量存储一个`rt_uint32_t`类型的数据用于读取和写入,存储一个`rt_uint16_t`类型的数据用于控制设备是否使能,能够操作。并实现四个接口分别用于实现设备使能状态的读取、写入,以及数据的读取和写入。 + +在驱动框架层使用驱动层提供的API实现IO设备管理层的接口。并实现驱动框架层自己的API供用于使用。即开发者既可以通过IO设备管理层操作该设备,也可以通过驱动框架层直接进行操作。 + +### 驱动框架层 +驱动框架层对接了IO设备管理层的接口,并使用了驱动层提供的接口,详细实现见[Day4my_vir.h](Day4my_vir.h)及[Day4my_vir.c](Day4my_vir.c)。驱动框架层的代码放在`rt-thread/components/drivers/`目录下,头文件放在`include/drivers`文件夹下,将本次的设备归类为`misc`杂项,源文件放在此目录下。 + +#### 头文件 +头文件中定义了用于接收下层设备接口的ops数据类型,以及本层的驱动框架层数据类型(此数据类型继承IO设备管理层的数据类型,并加入下层驱动层的ops)。 +```c +//定义设备驱动层的抽象接口 +struct my_vir_ops{ + void (*setstatus)(struct rt_device *device, rt_uint16_t status); + void (*getstatus)(struct rt_device *device, rt_uint16_t *status); + void (*writenumber)(struct rt_device *device, rt_uint32_t number); + void (*readnumber)(struct rt_device *device, rt_uint32_t *number); +}; + +//定义驱动框架层的数据类型 +struct my_vir_device { + struct rt_device parent;//继承父类 + const struct my_vir_ops *ops;//驱动层的接口 +}; +typedef struct my_vir_device *my_vir_device_t; +``` +#### 源文件 +驱动框架层使用驱动层的API实现了上层IO设备管理层的接口以及本层的特殊API `my_vir_read` 和 `my_vir_write`。 +```c +//对接IO设备管理层的init函数 +rt_err_t _my_vir_init(rt_device_t dev){ + rt_kprintf("init my vir\n"); + return RT_EOK; +} +//对接IO设备管理层的open函数 +rt_err_t _my_vir_open(rt_device_t dev, rt_uint16_t oflag){ + rt_kprintf("open my vir\n",oflag); + return RT_EOK; +} +//对接IO设备管理层的close函数 +rt_err_t _my_vir_close(rt_device_t dev){ + my_vir_device_t device = (my_vir_device_t)dev; + if(device->ops && device->ops->setstatus){ + rt_kprintf("close my vir\n"); + //将设备的状态参数设置为关闭 + device->ops->setstatus(dev, MY_VIR_CLOSE); + return RT_EOK; + } + return -RT_ERROR; +} +//对接IO设备管理层的read函数 +rt_size_t _my_vir_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size){ + my_vir_device_t device = (my_vir_device_t)dev; + if(device->ops->readnumber){ + //使用驱动层的API读取 + device->ops->readnumber(dev, (rt_uint32_t*)buffer); + return RT_EOK; + } + return -RT_ERROR; +} +//对接IO设备管理层的write函数 +rt_size_t _my_vir_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size){ + my_vir_device_t device = (my_vir_device_t)dev; + if(device->ops->writenumber){ + //使用驱动层的API写入 + device->ops->writenumber(dev, *(rt_uint32_t*)buffer); + return RT_EOK; + } + return -RT_ERROR; +} +//对接IO设备管理层的control函数,用于控制设备的使能状态 +rt_err_t _my_vir_control(rt_device_t dev, int cmd, void *args){ + rt_kprintf("control my vir\n"); + my_vir_device_t device = (my_vir_device_t)dev; + if(device->ops && device->ops->setstatus){ + //使用驱动层的API设置使能状态 + device->ops->setstatus(dev, *(rt_uint16_t*)args); + return RT_EOK; + } + return -RT_ERROR; +} +//实现注册函数,用于将设备注册到IO设备管理层 +rt_err_t rt_hw_my_vir_register(my_vir_device_t device, const char *name, const struct my_vir_ops *ops, const void *user_data) +{ + RT_ASSERT(ops != RT_NULL); + rt_err_t result; + + //设置驱动层接口 + device->ops = ops; + //向IO设备管理层注册函数 + device->parent.init = _my_vir_init; + device->parent.open = _my_vir_open; + device->parent.close = _my_vir_close; + device->parent.read = _my_vir_read; + device->parent.write = _my_vir_write; + device->parent.control = _my_vir_control; + + //注册设备 + result = rt_device_register(&device->parent, name, RT_DEVICE_FLAG_RDWR); + + return result; +} + +/** + * 此处实现的是驱动框架层的特殊API,开发者可以不通过IO设备管理层直接使用以下API + */ + +rt_err_t my_vir_read(my_vir_device_t dev, rt_uint32_t * val){ + RT_ASSERT(dev != RT_NULL && dev->ops != RT_NULL); + if(dev->ops->readnumber){ + rt_device_t device = (rt_device_t)dev; + dev->ops->readnumber(device, val); + return RT_EOK; + } + return -RT_ERROR; +} + +rt_err_t my_vir_write(my_vir_device_t dev, rt_uint32_t val){ + RT_ASSERT(dev != RT_NULL && dev->ops != RT_NULL); + if(dev->ops->writenumber){ + rt_device_t device = (rt_device_t)dev; + dev->ops->writenumber(device, val); + return RT_EOK; + } + return -RT_ERROR; +} +``` +#### 项目配置文件 +为了通过通过Kconfig文件控制设备的启动与否,进而配置参与编译的文件需要改写`Kconfig`文件,此处的`Kconfig`文件位于 `board` 目录下,将以下内容添加到 `menu "On-chip Peripheral Drivers"`下面 +``` +config RT_USING_MY_VIR + bool "Enable dev my vir" + default n +``` +之后就可以通过menuconfig来配置项目的宏,从而让对应文件参与编译。 +![meunconfig](figures/day4/menuconfig.png) + +#### 编译配置文件 +此外为了在使能的时候能够将对应的头文件包含进来需要改写`rt-thread/components/drivers/include/rtdevice.h` 文件在其中添加以下内容 +```c +#ifdef RT_USING_MY_VIR +#include "drivers/my_vir.h" +#endif +``` +以及`rt-thread/components/drivers/misc/SConscript`文件,添加以下信息,将源文件加入编译 +```python +if GetDepend(['RT_USING_MY_VIR']): + src = src + ['my_vir.c'] +``` + +### 设备驱动层 +设备驱动层实现具体的设备驱动逻辑,一般会使用到厂家提供的HAL等库,本次编写虚拟设备,不用实现真正的驱动逻辑,只需模拟操作并打印一些信息。详细实现见[Day4drv_my_vir.h](Day4drv_my_vir.h)及[Day4drv_my_vir.h](Day4drv_my_vir.c)。驱动层的代码存放在`libraries/HAL_Drivers`文件夹下。 + +#### 头文件 +驱动层实现具体硬件的操作,定义了针对具体硬件的数据结构,并继承驱动框架层的数据结构。 +```c +struct my_vir_test { + struct my_vir_device parent;//继承设备驱动框架层 + rt_uint32_t val;//设备保存的值 + rt_uint16_t status;//设备的状态 +}; +``` + +#### 源文件 +驱动层通过实现设备驱动框架层的接口来实现具体设备的操作。 +```c +//定义硬件相关的数据结构 +struct my_vir_test vir; +//实现驱动框架层的setstatus接口,用于设置设备状态。 +void setstatus(struct rt_device *device, rt_uint16_t status){ + vir.status = status; + rt_kprintf("the status is set to 0x%x\n", status); +} +//实现驱动框架层的getstatus接口,用于获取设备状态。 +void getstatus(struct rt_device *device, rt_uint16_t *status){ + *status = vir.val; +} +//实现驱动框架层的writenumber接口,用于向设备写入数据。 +void writenumber(struct rt_device *device, rt_uint32_t number){ + if(vir.status == MY_VIR_OPEN){ + vir.val = number; + return; + } + rt_kprintf("my vir is in close status\n"); +} +//实现驱动框架层的readnumber接口,用于从设备读取数据。 +void readnumber(struct rt_device *device, rt_uint32_t *number){ + if(vir.status == MY_VIR_OPEN){ + *number = vir.val; + return; + } + rt_kprintf("my vir is in close status\n"); +} +//定义本层所有实现的驱动框架层接口,用于对接。 +struct my_vir_ops ops = { + setstatus, + getstatus, + writenumber, + readnumber +}; +//初始化设备 +static int my_vir_init(void){ + vir.status = MY_VIR_CLOSE;//初始时设备状态为关闭 + //对接接口 + rt_hw_my_vir_register(&vir.parent, "my_vir", &ops, (void*)&vir.status); +} +//自动初始化 +INIT_APP_EXPORT(my_vir_init); +``` + +#### 编译配置文件 +为了将对应的驱动层文件在编译的时候能够包含进来需要改写`libraries/HAL_Drivers/SConscript`文件,在其中添加以下内容 +```python +if GetDepend(['RT_USING_MY_VIR']): + src += ['drv_my_vir.c'] +``` +然而实际上改写SConsScript文件并不生效不知道为什么,在对应.c文件右键`资源配置/添加构建`之后才成功添加构建。 + +### main函数调用 +main函数中主要通过IO设备驱动层的调用以及驱动框架层的调用,两种不同的方式使用设备,详细代码见[Day4main.c](Day4main.c) + +### 实验结果 +![实验结果](figures/day4/result.png) \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/compile_1.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/compile_1.png" new file mode 100644 index 0000000000000000000000000000000000000000..a80eee2dc6278b6d7f5e60ab4fe55a3cd8f48268 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/compile_1.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/compile_2.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/compile_2.png" new file mode 100644 index 0000000000000000000000000000000000000000..18554ad97a39f7771f2f140d0b525935f6af8186 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/compile_2.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/boot.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/boot.png" new file mode 100644 index 0000000000000000000000000000000000000000..cab5f198c08ccff8891d00fcf331a13cce911898 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/boot.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/entry.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/entry.png" new file mode 100644 index 0000000000000000000000000000000000000000..df40cd0f0fab96b173c4186289f7936b7705eecc Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/entry.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/install_packages.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/install_packages.png" new file mode 100644 index 0000000000000000000000000000000000000000..5c7043943fbb7ec36db61590308a139b5e542b44 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/install_packages.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/project0.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/project0.png" new file mode 100644 index 0000000000000000000000000000000000000000..629449d465125f44afcbc89f47a4afa656fc8c32 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/project0.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/project1_code.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/project1_code.png" new file mode 100644 index 0000000000000000000000000000000000000000..4544cc66baac5481d69cd29049b27e511827bead Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/project1_code.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/project1_config.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/project1_config.png" new file mode 100644 index 0000000000000000000000000000000000000000..790a956a9cf6f49addf8e441338dbcc9663c1201 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/project1_config.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/project1_run.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/project1_run.png" new file mode 100644 index 0000000000000000000000000000000000000000..d68e0176e5add0b6fe9d987656cf3edb203c67c9 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/project1_run.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/rtt_startup.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/rtt_startup.png" new file mode 100644 index 0000000000000000000000000000000000000000..04dbb79fed05b7463c5ceea179f5dd75c6553724 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/rtt_startup.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/runtime_status.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/runtime_status.png" new file mode 100644 index 0000000000000000000000000000000000000000..58564d0e8c0e5b9a911b83bd7fb8b3e4578a58d4 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/runtime_status.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/schedule.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/schedule.png" new file mode 100644 index 0000000000000000000000000000000000000000..5f547a334772280faa91dac3bbfe7b4f15747b3c Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/schedule.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/stack_frame.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/stack_frame.png" new file mode 100644 index 0000000000000000000000000000000000000000..7d544624d4230d294145f8121a4761ba9f951dcd Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/stack_frame.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/startup.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/startup.png" new file mode 100644 index 0000000000000000000000000000000000000000..b464afed48f85bbdc1b6d86cfb3f3cdc5a360803 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/startup.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/studio_ui.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/studio_ui.png" new file mode 100644 index 0000000000000000000000000000000000000000..83dacdd58b97b3c023b69928a5858687503ff39b Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day2/studio_ui.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day3/count_sample.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day3/count_sample.png" new file mode 100644 index 0000000000000000000000000000000000000000..5a69a8108857665bb5a2ac21ad6693297987bf37 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day3/count_sample.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day3/event_sample.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day3/event_sample.png" new file mode 100644 index 0000000000000000000000000000000000000000..152d02fd942215f3ab19f9564d9b266a13ed7ac6 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day3/event_sample.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day3/mainbox.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day3/mainbox.png" new file mode 100644 index 0000000000000000000000000000000000000000..fd12377fa701ab5f8ff580cc004e260268731967 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day3/mainbox.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day3/msg_queue.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day3/msg_queue.png" new file mode 100644 index 0000000000000000000000000000000000000000..f9f7ad68f3d9caa955066c9a5480d4ea3ff1c1a1 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day3/msg_queue.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day3/sync_sample.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day3/sync_sample.png" new file mode 100644 index 0000000000000000000000000000000000000000..19b22d08a901d3665ca31ac002d8ddd4341f28de Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day3/sync_sample.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/class_diagram.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/class_diagram.png" new file mode 100644 index 0000000000000000000000000000000000000000..ea51e75f9e64ac6000177cb52c300ae669520250 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/class_diagram.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/device_access.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/device_access.png" new file mode 100644 index 0000000000000000000000000000000000000000..579f904d4177cdebc53c4725f650119854dccb32 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/device_access.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/i2c_protocol.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/i2c_protocol.png" new file mode 100644 index 0000000000000000000000000000000000000000..1d0e22ec0fed7c98d2133594687090c7856158cd Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/i2c_protocol.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/i2c_read.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/i2c_read.png" new file mode 100644 index 0000000000000000000000000000000000000000..55c7726c5ecf40f388fd04ebe40d239731c5635b Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/i2c_read.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/i2c_write.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/i2c_write.png" new file mode 100644 index 0000000000000000000000000000000000000000..1c2fd4c72e4282845d02d1c2b5eae6ea6c54dd2f Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/i2c_write.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/io_invoke_relationship.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/io_invoke_relationship.png" new file mode 100644 index 0000000000000000000000000000000000000000..fd892ec5ff06784738ae53e9a33281985b3463a7 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/io_invoke_relationship.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/sequence_diagram.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/sequence_diagram.png" new file mode 100644 index 0000000000000000000000000000000000000000..86d5f0c6e97cf7c296791400ef589874813fb4be Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/sequence_diagram.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/spi.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/spi.png" new file mode 100644 index 0000000000000000000000000000000000000000..d17e4cbfdaa98cb03929dceeb8ef3fff592bcde0 Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/spi.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/structure1.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/structure1.png" new file mode 100644 index 0000000000000000000000000000000000000000..bb764d511a8218fe905fbc6004fc6c0b8a93d4cd Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/structure1.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/structure2.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/structure2.png" new file mode 100644 index 0000000000000000000000000000000000000000..4b4bca35e00b4729e2b4b4df105f3f750e46e33d Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/structure2.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/trigger_mode.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/trigger_mode.png" new file mode 100644 index 0000000000000000000000000000000000000000..bf60bacbbc245c993ebeca6b42b376abf768e8ca Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/day4/trigger_mode.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/gui.png" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/gui.png" new file mode 100644 index 0000000000000000000000000000000000000000..575ba006d67c2109067fca506b9215c04e4586ac Binary files /dev/null and "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/figures/gui.png" differ diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/\347\254\2541\345\244\251\347\254\224\350\256\260.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/\347\254\2541\345\244\251\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..9b979a22e90989a2a961fd8d3158580b511469d6 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/\347\254\2541\345\244\251\347\254\224\350\256\260.md" @@ -0,0 +1,124 @@ +## 环境搭建与测试 + +### Git +安装版本管理软件[Git](https://git-scm.com/downloads)用于项目源代码仓库的管理和版本控制。 + +### 拉取RT-Thread项目源码 +从[github(国外)](https://github.com/RT-Thread/rt-thread)或[gitee(国内)](https://gitee.com/rtthread/rt-thread)代码仓库获取源码,使用命令 + +```sh +git clone https://gitee.com/rtthread/rt-thread.git +``` + +### 安装env工具 +从[github仓库](https://github.com/RT-Thread/env-windows)、[gitee仓库](https://gitee.com/mirrors_RT-Thread/env-windows)拉取或[官网](https://www.rt-thread.org/download.html)下载,官网的为离线版本,且版本号更新一点。其中使用了sub module,使用以下命令拉取 + +```sh +git clone --recursive --depth 1 https://github.com/RT-Thread/env-windows.git + +git clone --recursive --depth 1 https://gitee.com/mirrors_RT-Thread/env-windows.git +``` + +打开目录下的 `env.bat` 文件等待环境配置完成。 + +在命令窗口的右上角 `settings -> Integration -> Register` 将 `env` 注册到Windows右键菜单中。方便之后在对应目录打开 `env` + +先进行一次 `menuconfig` 生成配置文件,直接保存退出。 + +使用以下命令更新 +```sh +pkgs --update #更新包 + +pkgs --upgrade #更新env +``` + +可以使用 `help` 命令查看所有命令 + +### RT-Thread目录结构 +|名称 |描述 | +|-------------|---------------------------------------------------| +|bsp |Board Support Package(板级支持包)基于各种开发板的移植| +|components |RT-Thread 的各个组件代码,例如 finsh,gui 等。 | +|documentation|相关文档,如编码规范等 | +|examples |相关示例代码 | +|include |RT-Thread 内核的头文件。 | +|libcpu |各类芯片的移植代码。 | +|src |RT-Thread 内核的源文件。 | +|tools |RT-Thread 命令构建工具的脚本文件。 | + +### 尝试针对qemu平台编译 +在 `rt-thread\bsp\qemu-vexpress-a9` 目录下打开 `env` 工具,并使用menuconfig生成配置文件,直接保存退出即可。 + +使用以下命令编译,其中的数字表示编译使用的线程数。最终编译生成elf文件。 +```sh +scons -j4 #编译 +scons -c #清除编译文件 +``` + +![编译](figures/compile_1.png) + +使用以下命令启动,进入shell界面 +```sh +qemu-nographic.bat +``` + +在shell界面可以使用以下命令 +```sh +list device #查看当前所有的设备 +list thread #查看当前启动的线程 +list timer #当前使用到的定时器 +Ctrl+A松开后再按X #退出终端 +``` +尝试修改 `applications` 目录下的 `main.c` 文件后重新编译运行。 +![修改main后重新编译](figures/compile_2.png) + +### 编译过程概述 +项目的编译依赖于python脚本包括项目根目录的 `SConstruct` 、 `SConscript` 文件 以及各个目录下的`SConscript` 文件,通过递归查找目录下的 `.c` 和 `.cpp` 文件并返回,最终形成编译命令。 + +### 重新配置项目实现图像显示 +在 `rt-thread\bsp\qemu-vexpress-a9` 目录下打开 `env` 工具,并使用menuconfig生成配置文件,打开 `Hardware Drivers Config -> Onboard Peripheral Drivers -> Enable LVGL for LCD` 返回保存,更新 `Kconfig` 文件,同时会同步到 `.config` 文件中并生成 `rtconfig.h` 文件。 + +LVGL是一个软件包使用 `pkgs --update` 。实际上在保存退出后就会默认进行下载。下载的文件在根目录packages文件夹下。 + +再次编译生成elf文件并使用以下命令启动带有图形界面的终端。 +```sh +qemu.bat +``` +![图形界面](figures/gui.png) + + +### 安装VSCode +在[官网](https://code.visualstudio.com/)安装VSCode用于编辑代码及后续调试等 + +## Git使用及提交PR +Git主要用于项目的版本管理及多人协作。 + +### .git文件夹 +项目根目录下有.git文件夹,其中存储了和git 相关的文件,包括历史记录及一些配置主要的配置可以在 `config` 文件中查看。 + +### 常用命令 +```sh +git init #初始化文件夹成为一个git仓库 +git status #查看当前状态 +git add #暂存文件,将文件放到暂存区 +git log #查看历史 +git switch #切换分支 +git checkout #回退,迁出分支 +git reset --soft/--hard HEAD~ #重置,soft保留操作到暂存区hard直接删除 +git branch/checkout -b #创建新的分支 +git commit -m "本次提交说明" #提交代码 +git merge #合并分支 +``` + +### 使用ssh拉取仓库 +在第一次使用时需要创建一对密钥完成ssh配置(gitee平台) +```sh +ssh-keygen -t rsa #创建密钥对 +``` +windows环境下使用OpenSSH密钥对默认的存储位置为 `C:/Users/UserName/.ssh/` 默认文件名称为 `id_rsa(私钥)` 和 `id_rsa.pub(公钥)` 生成密钥时最好不要修改名称,修改名称会涉及到配置该目录下的 `config` 文件。使用以下命令测试配置正确性。 +```sh +ssh -T git@gitee.com +``` + +### 提交PR +首先对目标仓库进行 `fork` 操作将其拷贝到自己的仓库。本地完成编辑之后推送到自己的仓库。最后在目标仓库的PR列表提交PR请求。 \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/\347\254\2542\345\244\251\347\254\224\350\256\260.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/\347\254\2542\345\244\251\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..71fbcd99954a90d22c7995db43a234338e237f10 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/\347\254\2542\345\244\251\347\254\224\350\256\260.md" @@ -0,0 +1,129 @@ +## RT-Thread内核基础与线程 + +### 裸机与RTOS +功能单一的情况下,系统整体功能没有细分多个线程的必要,采用裸机较为合适。例如电动牙刷 + +系统功能丰富情况下,使用裸机编程需要编写复杂的逻辑协调多个事件,同时代码的耦合度较高,采用RTOS较为合适。例如机器人。 + +裸机优点与缺点: +- 单一功能的嵌入式系统使用裸机可以提高运行效率 +- 可以使用较少的存储实现单一功能的嵌入式系统 +- 代码耦合度较高,复用性差 +- 不适合复杂的嵌入式系统 +- 代码不合理时容易造成系统阻塞 + +RTOS优点与缺点: +- 代码耦合度低,复用性好 +- 可以使用任务划分的方式降低实现复杂嵌入式操作系统的代码逻辑 +- 延时操作不会一直占用CPU,延时期间处理其他任务 +- 使用RTOS提供的线程同步与信息传递会提高系统的实时性 +- 不适合小容量嵌入式系统,RTOS本身会占据部分存储 +- 不适合单一嵌入式系统,系统调度会引入额外的开销 + +> RTOS编程的思想为模块化“分而治之”的拆分思想,降低每个任务的复杂性。 + +下图为两种程序的运行情况 +![运行情况](figures/day2/runtime_status.png) + +### 临界区 +临界区的资源在同一时间只能被一个线程使用,一但临界资源被占用,其他线程只能等待。 + +一个线程先占用了临界区的资源,其他线程想使用临界资源就必须等待,这种阻塞其他线程继续执行的情况就是线程阻塞(Blocking) + +### 系统启动 +如下图为RT-Thread的系统启动过程 +![启动过程](figures/day2/rtt_startup.png) + +其中启动文件 `startup_xx.S` 对flash、ram等硬件介质进行初始化。之后根据不同的编译环境(MDK、IAR、GCC)进入不同的函数。如图为STM32F407的板级开发包,其中的启动文件中调用了entry函数。 +![启动文件](figures/day2/startup.png) + +而在 `component.c` 文件中又根据不同的编译环境分别写了预编译命令。如图中框出的为gcc环境下的entry函数,其中调用了又调用了 `rtthread_startup` 函数。 +![entry](figures/day2/entry.png) + +在 `rtthread_startup` 中首先进行了关中断的操作 `rt_hw_interrupt_disable`,避免外部的中断打断操作系统的初始化。 + +`rt_hw_board_init` 进行一些板级外设硬件的初始化。初始化外设之后就可以通过 `rt_show_version` 从串口打印信息。 + +对于 `rt_system_timer_init` 和 `rt_system_timer_thread_init` 他们用于初始化系统中的定时器。前者是系统中使用硬件中断计时的,后者是软件定时器对线程进行计时,前者的精度更高。 + +`rt_system_scheduler_init` 用于初始化调度器的队列 + +`rt_system_signal_init` 软件中断的初始化 + +`rt_application_init` 和 `rt_thread_idle_init` 后者创建了一个空闲线程,若系统中没有要运行的线程,则调度空闲线程,它是优先级最低的一个线程它永远处于就绪状态不会被挂起。其中适合使用一些钩子函数进行功耗管理看门狗喂狗等操作。前者创建main函数的线程。 + +`rt_system_scheduler_start` 启动系统开始进行线程调度。在main函数执行之前还有一部分软件初始化,如协议栈、驱动、软件包等的初始化。 + +### 线程调度 +若某线程运行完毕,系统将自动删除线程:自动执行 `rt_thread_exit` 函数,先将该线程从系统就绪队列中删除,再将该线程的状态更改为关闭,不再参与系统调度,然后挂入 `rt_thread_defunct` 僵尸队列(资源未回收、处于关闭状态的线程队列),最后空闲线程会回收被删除线程的资源。 + +### 线程创建 +一个线程要成为可执行的对象,就必须由操作系统的内核来为它创建一个线程,可以通过一下接口创建一个动态线程。 +```c +rt_thread_t rt_thread_create(const char*name, + void (*entry)(void* parameter), + void* parameter, + rt_uint32_t stack_size, + rt_uint8_t priority, + rt_uint32_t tick); +``` +其中参数 `name` 用于找到线程的控制块,最终获得句柄,通过句柄获得线程的状态等信息。 `priority` 是优先级,数字越小优先级越高。 `tick` 是时间片大小。 + +调用此函数时,系统从动态堆内存中分配一个线程句柄以及按照参数中指定的栈大小相应的空间。分配出来的栈空间按照 `rtconfig.h` 中配置的 `RT_ALIGN_SIZE` 方式对齐。 + +创建线程还可以使用函数 `rt_thread_init` 创建线程,两者区别在于此函数是静态创建线程,而create函数是动态创建线程。静态创建在编译阶段就分配了空间,只为此线程使用更安全,无论后续线程处于什么状态都不会给其他线程使用。系统的几乎所有线程都是静态分配的。动态创建使用动态分配的方式分配空间,后续可以回收。使用init创建的线程使用函数 `rt_thread_detach` 进行分离(从就绪队列移除),使用create创建的函数则用函数 `rt_thread_delete` 释放空间。 + +### 其他线程相关函数 +`rt_thead_self` 获取当前运行的线程 +`rt_thead_find` 通过名称寻找线程控制块 +`rt_thead_startup` 启动线程进入就绪状态(创建后状态处于初始状态还未加入调度) +`rt_thead_yield` 立刻放弃当前线程的时间片 +`rt_thead_delay` 延时,主动释放CPU +`rt_thead_control` 修改线程的优先级等控制信息 +`rt_thead_suspend` 挂起线程,只能对自己线程操作 +`rt_thead_resume` 恢复线程 + +### 线程创建的一些原理 +两种创建都调用了函数 `_thread_init` 该函数除了进行回调函数的初始化之外还调用了一个重要的函数 `rt_hw_stack_init` 其中对栈帧进行了初始化。由于该线程还未被调用初始化栈帧实际相当于手动设置了现场。 + +栈帧结构体 `stack_frame` 其中又包含了一个结构体 `exception_stack_frame`。他们的区别涉及到调用约定。如下图,其中红色的为被调用者保存寄存器,需要被调用函数保存/恢复。绿色中的r0-r3是调用者保存寄存器,函数调用时进行临时传参。此规定与具体芯片有关。此外由于Cortex-M3/M4支持硬件压栈、恢复, `exception_stack_frame` 中的寄存器可以由硬件自动保存,而 `stack_frame` 中的其他寄存器就需要操作系统手动保存。 +![stack_frame](figures/day2/stack_frame.png) + +此外创建线程中还初始化了定时器,用于线程中的delay操作。 + +### 线程状态的转化 +![线程状态转化](figures/day2/schedule.png) + +无循环的线程执行完毕后系统自动回收资源,循环的线程会通过系统延时主动让出CPU或等待IPC。 + +### 调度器的工作 +- 优先级抢占:保证一个最高优先级的任务运行,从就绪链表中查找最高优先级的任务。 +- 时间片轮转:相同优先级状态下根据时间片轮转运行。 + +## RT-Thread Studio简单示例 +首先安装[RT-Thread Studio](https://www.rt-thread.org/studio.html) + +其中主要需要的功能如下图所示 +![studio_ui](figures/day2/studio_ui.png) + +此示例主要针对stm32F407板级开发包。 + +### stm32F407板级开发包安装 +首先在SDK管理中勾选 `Board_Support_Packages/STMicroelectronics/STM32F407-ATK-EXPLORE` 的最新版以及 `Debuger_Support_Packages/QEMU` 的最新版,并安装资源包。 +![安装资源包](figures/day2/install_packages.png) + +之后新建RT-Thread项目,选择基于开发版,选中刚下载的开发板及调试器和对应的模拟器。创建工程后选中项目处于激活状态并编译项目。编译成功后点击下载按钮选择对应的模拟器运行代码,进入msh。 +![项目0](figures/day2/project0.png) + +### 安装线程软件包 +在工程目录下的 `RT-Thread Settings` 中打开 `软件包 -> 杂项软件包 -> 软件包内核示例/thread` 再按Ctrl+S保存完成软件包的拉取与配置。 +![项目1配置](figures/day2/project1_config.png) + +编译运行后在shell运行 `thread_sample` 查看示例 +![项目1运行](figures/day2/project1_run.png) + +可以看到代码中分别通过动态和静态的方式创建了共2个线程。 +![项目1代码](figures/day2/project1_code.png) + +### 编写代码体现抢占与时间片轮转 +详见[第2天作业](../作业/第2天作业.md) \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/\347\254\2543\345\244\251\347\254\224\350\256\260.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/\347\254\2543\345\244\251\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..5450bb78ed6d08bdb0ef983451b92a5345a101f5 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/\347\254\2543\345\244\251\347\254\224\350\256\260.md" @@ -0,0 +1,282 @@ +# IPC机制 +RT-Thread的IPC机制分为线程同步和线程间通信。 + +线程同步有以下机制 +- 信号量(Semaphore) +- 互斥量(Mutex) +- 事件集(Event) + +线程间通信有以下机制 +- 邮箱(mailbox) +- 消息队列(msgqueue) + +## 线程间同步 +### 信号量 +信号量(Semaphore)是一种轻型的用于解决线程间同步问题的内核对象,一个或多个运行线程可以获取或释放它,从而达到同步或互斥的目的。用于实现任务与任务之间、任务与中断程序之间的同步与互斥。 + +信号量一般分为三种 +- 互斥信号量:解决互斥问题。但可能引起优先级反转的问题,因此一般不使用 +- 二值信号量:解决同步问题。 +- 计数信号量:解决资源计数问题。 + +二值信号量主要用于线程与线程之间、线程与中断服务程序(ISR)之间的同步。用于同步分二值信号量初始值为0,表示同步事件尚未产生。一个线程获取信号量等待事件发生,另一个线程或ISR到达同步点时释放信号量,从而唤醒等待的任务。信号量为0时不可获得,为1时可以获得。原理如图所示。 +![同步原理](figures/day3/sync_sample.png) + +计数信号量用于控制系统中共享资源的多个实例的使用,允许多个线程同时访问同一种资源的多个实例。计数信号量初始化为n(非负整数),n为共享资源的数目。计数信号量在线程申请并获得的时候减1,在释放时加1,值为0时不可获得。原理如图所示。 +![计数原理](figures/day3/count_sample.png) + +#### 信号量API +创建与删除信号量的API分别如下所示 +```c +rt_sem_t rt_sem_create(const char* name, + rt_uint32_t value, + rt_uint8_t flag); + +rt_err_t rt_sem_delete(rt_sem_t sem); +``` +创建信号量时,系统将先从对象管理器中分配一个semaphore对象,并初始化这个对象,然后初始化父类IPC对象以及与semaphore相关的部分。在创建信号量指定的参数中,信号量标志参数决定了当信号量不可用时,多个线程等待的排队方式。当选择RT_IPC_FLAG_FIFO(先进先出)方式时,那么等待线程队列将按照先进先出的方式排队,先进入的线程将先获得等待的信号量;当选择RT_IPC_FLAG_PRIO(优先级等待)方式时,等待线程队列将按照优先级进行排队,优先级高的等待线程将先获得等待的信号量。 + +删除信号量时,如果有现成正在等待该信号量,那么删除操作会先唤醒等待在该信号量上的线程(等待线程的返回值为 `-RT_ERROR` ),然后再释放信号量的内存资源。 + +信号量初始化与脱离,与create函数区别在于该函数静态创建信号量。脱离函数让静态创建的信号量对象从内核对象管理器中脱离。同样会唤醒等待的线程获得 `-RT_ERROR` 的返回值。 +```c +rt_err_t rt_sem_init(rt_sem_t sem, + const char *name, + rt_uint32_t value, + rt_uint8_t flag); + +rt_err_t rt_sem_detach(rt_sem_t sem); +``` + +获取信号量,用于获得信号量资源实例。信号量大于0时获得信号量,相应信号量值减1。信号量值等于0时,说明信号量资源实例不可用,申请的线程会根据time参数的情况选择直接返回,挂起等待一段时间或永久等待。若time时间内仍然得不到,线程将超时返回 `-RT_ETIMEOUT`。 +```c +rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time); + +rt_err_t rt_sem_trytake(rt_sem_t sem);//无等待获取信号量 +``` + +释放信号量,唤醒挂起在该信号量上的线程。若无等待,值加1。 +```c +rt_err_t rt_sem_release(rt_sem_t sem); +``` + +### 互斥量 +互斥量是一种特殊的二值信号量。它和信号量不同的是,它支持: +- 互斥量所有权:互斥量具有线程所有权,只有加锁的线程才能解锁,否则可能导致未定义行为(如死锁)。信号量则没有这个概念。 +- 递归访问 +- 防止优先级反转的特性。 + +> 优先级反转:当一个高优先级线程试图通过信号量机制访问共享资源时,如果该信号量已被一低优先级线程持有,而这个低优先级线程在运行过程中可能又被其它一些中等优先级的线程抢占,因此造成高优先级线程被许多具有较低优先级的线程阻塞,实时性难以得到保证。 + +互斥量解决优先级反转的原理为它实现了优先级继承协议。通过在一个高优先级线程A尝试获取共享资源而被挂起的期间,将低优先级线程C优先级提高到A的优先级别。 + +#### 互斥量API +创建与删除互斥量的API分别如下所示 +```c +rt_sem_t rt_mutex_create(const char* name, rt_uint8_t flag); + +rt_err_t rt_mutex_delete(rt_mutex_t mutex); +``` +创建互斥量时,它的名字由name所指定。当调用这个函数时,系统将先从对象管理器中分配一个mutex对象,并初始化这个对象,然后初始化父类IPC对象以及与mutex相关的部分。互斥量的fIag标志已经作废,无论用户选择RT_IPC_FLAG_PRIO还是RT_IPC_FLAG_FIFO,内核均按照RT_IPC_FLAG_PRIO处理。 + +删除互斥量时,所有等待此互斥量的线程都将被唤醒(等待线程的返回值为 `-RT_ERROR` ),然后系统将互斥量从内核对象管理器链表中删除并释放空间。 + +互斥量初始化与脱离,与create函数区别在于该函数静态创建互斥量。脱离函数让静态创建的互斥量对象从内核对象管理器中脱离。同样会唤醒等待的线程获得 `-RT_ERROR` 的返回值。 +```c +rt_err_t rt_mutex_init(rt_mutex_t mutex, + const char *name, + rt_uint8_t flag); + +rt_err_t rt_mutex_detach(rt_mutex_t mutex); +``` + +获取互斥量,现成拥有了对互斥量的所有权,即某一时刻一个互斥量只能被一个线程持有。如果互斥量已经被当前线程控制,则持有计数加1。如果被其他线程占有,当前线程在互斥量上挂起等待,直到其他线程释放或者等待超时。 +```c +rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time); + +rt_err_t rt_mutex_trytake(rt_mutex_t mutex);//无等待获取互斥量 +``` + +释放互斥量,完成互斥资源访问后应尽快释放占据的互斥量,使其他线程能即时获取。每释放一次持有计数减1,计数为0时变为可用,等待在该互斥量上的线程将被唤醒。若线程的优先级被提升,则恢复。 +```c +rt_err_t rt_sem_release(rt_sem_t sem); +``` + +### 事件集 +事件集是一个32bit的数,每个事件用一个bit位代表,触发方式有与触发、或触发。 +- 发送:可以从中断或者线程中进行发送 +- 接收:线程接收,条件检查(逻辑与方式、逻辑或方式) +![事件集示例](figures/day3/event_sample.png) + +#### 事件集API +创建与删除事件,创建时系统从对象管理器中分配事件集对象,并初始化,然后初始化父类IPC对象。系统不再使用创建的事件集对象时,通过删除事件集对象控制块释放系统资源。 +```c +rt_event_t rt_event_create(const char* name, rt_uint8_t flag); + +rt_err_t rt_event_delete(rt_event_t event); +``` +删除一个事件集对象时应确保该事件集不再被使用。删除前会唤醒所有挂起在该事件集上的线程(线程的返回值为 `-RT_ERROR`)然后释放事件集对象占用的内存块。 + +事件集初始化与脱离,与create函数区别在于该函数静态创建事件集,并加入系统对象容器中管理。脱离函数让静态创建的事件集对象从内核对象管理器中脱离。同样会唤醒等待的线程获得 `-RT_ERROR` 的返回值。 +```c +rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag); + +rt_err_t rt_event_detach(rt_event_t event); +``` + +发送事件,可以发送事件集的一个或多个事件。其中通过set是定事件集对象的时间标志值,然后遍历等待在event事件集对象上的等待线程链表,判断是否有线程的事件激活要求与当前event对象事件标志值匹配,若有则唤醒。 +```c +rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set); +``` + +接收事件,内核使用32位的无符号整数来标识事件集,每一位代表一个事件,因此一个事件集对象可同时等待接收32个事件,内核可以通过指定选择参数“逻辑与”或“逻辑或”来选择如何激活线程。 +```c +rt_err_t rt_event_recv(rt_event_t event, + rt_uint32_t set, + rt_uint8_t option, + rt_int32_t timeout, + rt_uint32_t* recved); +``` +系统首先根据set参数和接收选项option来判断它要接收的事件是否发生,如果已经发生,则根据参数option上是否设置有 `RT_EVENT_FLAG_CLEAR` 来决定是否重置事件的相应标志位,然后返回(其中recved参数返回接收到的事件);如果没有发生,则把等待的set和option参数填入线程本身的结构中,然后把线程挂起在此事件上,直到其等待的事件满足条件或等待时间超过指定的超时时间。如果超时时间设置为零,则表示当线程要接受的事件没有满足其要求时就不等待,而直接返回 `-RT_ETIMEOUT`。 + +## 线程间通信 +### 邮箱 +RT-Thread操作系统的邮箱用于线程间通信,特点是开销比较低,效率较高。邮箱中的每一封邮件只能容纳固定的4字节内容(针对32位处理系统,指针的大小即为4个字节,所以一封邮件恰好能够容纳一个指针)。如下图所示,线程或中断服务例程把一封4字节长度的邮件发送到邮箱中,而一个或多个线程可以从邮箱中接收这些邮件并进行处理。 +![邮箱](figures/day3/mainbox.png) + +邮件发送过程非阻塞,能够安全的应用于中断服务中,是线程、中断服务、定时器向线程发送消息的有效手段。通常来说,邮件收取过程可能是阻塞的,这取决于邮箱中是否有邮件,以及收取邮件时设置的超时时间。当邮箱中不存在邮件且超时时间不为0时,邮件收取过程将变成阻塞方 +式。在这类情况下,只能由线程进行邮件的收取。 + +当一个线程向邮箱发送邮件时,如果邮箱没满,将把邮件复制到邮箱中。如果邮箱已经满了,发送线程可以设置超时时间,选择等待挂起或直接返回 `-RT_EFULL`。如果发送线程选择挂起等待,那 +么当邮箱中的邮件被收取而空出空间来时,等待挂起的发送线程将被唤醒继续发送。当一个线程从邮箱中接收邮件时,如果邮箱是空的,接收线程可以选择是否等待挂起直到收到新的邮件而唤醒,或可以设置超时时间。当达到设置的超时时间,邮箱依然未收到邮件时,这个选择超时等待的线程将被唤醒并返回 `-RT_ETIMEOUT`。如果邮箱中存在邮件,那么接收线程将复制邮箱中的4个字节邮件到接收缓存中。 + +#### 邮箱API +邮箱的创建与删除 +```c +rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag); + +rt_err_t rt_mb_delete(rt_mailbox_t mb); +``` +创建邮箱对象时会先从对象管理器中分配一个邮箱对象,然后给邮箱动态分配一块内存空间来存放邮件,这块内存的大小等于邮件大小(4字节)与邮箱容量的乘积,接着初始化接收邮件数目和发送邮件在邮箱中的偏移量。删除邮箱时,如果有线程被挂起在该邮箱对象上,内核先唤醒挂起在该邮箱上的所有线程(线程返回值是 `-RT_ERROR`),然后再释放邮箱使用的内存,最后删除邮箱对象。 + +邮箱的初始化与脱离 +```c +rt_err_t rt_mb_init(rt_mailbox_t mb, + const char* name, + void* msgpool, + rt_size_t size, + rt_uint8_t flag); + +rt_err_t rt_mb_detach(rt_mailbox_t mb); +``` +同样与create的区别为静态创建,以及把静态初始化的邮箱对象从内核对象管理器中脱离。 + +发送邮件 +```c +rt_err_t rt_mb_send(rt_mailbox_t mb, rt_uint32_t value); + +rt_err_t rt_mb_send_wait(rt_mailbox_t mb, + rt_uint32_t value, + rt_int32_t timeout);//有等待时间 + +rt_err_t rt_mb_urgent(rt_mailbox_t mb, rt_ubase_t value); +``` +线程或者中断服务程序可以通过邮箱给其他线程发送邮件。发送的邮件可以是32位任意格式的数据,一个整型值或者一个指向缓冲区的指针。当邮箱中的邮件已经满时,发送邮件的线程或者中断程序会收到 `-RT_EFULL` 的返回值。 + +对于wait函数,如果邮箱已经满了,那么发送线程将根据设定的timeout参数等待邮箱中因为收取邮件而空出空间。如果设置的超时时间到达依然没有空出空间,这时发送线程将被唤醒并返回错误码。 + +对于urgent函数,发送紧急邮件时,邮件被直接插队放入了邮件队首,这样,接收者就能够优先接收到紧急邮件,从而及时进行处理。 + +接收邮件 +```c +rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_uint32_t *value, rt_int32_t timeout); +``` +只有当接收者接收的邮箱中有邮件时,接收者才能立即取到邮件并返回RT_EOK的返回值,否则接收线程会根据超时时间设置,或挂起在邮箱的等待线程队列上,或直接返回。接收邮件时,接收者需指定接收邮件的邮箱句柄,并指定接收到的邮件存放位置以及最多能够等待的超时时间。如果接收时设定了超时,当指定的时间内依然未收到邮件时,将返回 `-RT_ETIMEOUT`。 +### 消息队列 +消息队列,也就是将多条消息排成的队列形式,是一种常用的线程间通信方式,可以应用在多种场合,线程间的消息交换,使用串口接收不定长数据等。线程可以将一条或多条消息放到消息队列中,同样一个或多个线程可以从消息队列中获得消息;同时消息队列提供异步处理机制可以起到缓冲消息的作用。 +![消息队列](figures/day3/msg_queue.png) + +使用消息队列实现线程间的异步通信工作,具有以下特性: +- 支持读消息超时机制 +- 支持等待方式发送消息 +- 允许不同长度(不超过队列节点最大值)任意类型消息 +- 支持发送紧急消息 +#### 消息队列API +创建消息队列时先从对象管理器中分配一个消息队列对象,然后给消息队列对象分配一块内存空间,组织成空闲消息链表,`这块内存的大小=[消息大小+消息头(用于链表连接的大小)]*消息队列最大个数`,接着再初始化消息队列;接口返回`RT_EOK`表示动态消息队列创建成功。 +```c +rt_mq_t rt_mq_create (const char*name,//消息队列名称 + rt_size_t msg_size,//消息队列中一条消息的最大长度,单位字节 + rt_size_t max_msgs,//消息队列的最大个数 + rt_uint8_t flag);//消息队列采用的等待方式 +``` +参数fIag可以取如下数值:RT_IPC_FLAG_FIFO(先进先出)或RT_IPC_FLAG_PRIO(优先级等待) + +消息发送 +```c +rt_err_t rt_mq_send (rt_mq_t mq,//消息队列对象的句柄 + void* buffer,//消息内容 + rt_size_t size);//消息大小 + +rt_err_t rt_mq_send_wait(rt_mq_t mq,//消息队列对象的句柄 + const void *buffer,//消息内容 + rt_size_t size,//消息大小 + rt_int32_t timeout);//超时时间 + +rt_err_t rt_mq_urgent (rt_mq_t mq,//消息队列对象的句柄 + void *buffer,//消息内容 + rt_size_t size);//消息大小 +``` +线程或者中断服务程序都可以给消息队列发送消息。当发送消息时,消息队列对象先从空闲消息链表上取下一个空闲消息块,把线程或者中断服务程序发送的消息内容复制到消息块上,然后把该消息块挂到消息队列的尾部。当且仅当空闲消息链表上有可用的空闲消息块时,发送者才能成功发送消息;当空闲消息链表上无可用消息块,说明消息队列已满,此时,发送消息的的线程或者中断程序会收到一个错误码(`-RT_EFULL`)。 + +对于wait函数,如果消息队列已经满了,那么发送线程将根据设定的timeout参数进行等待。如果设置的超时时间到达依然没有空出空间,这时发送线程将被唤醒并返回错误码。 + +对于ergent函数,当发送紧急消息时,从空闲消息链表上取下来的消息块不是挂到消息队列的队尾,而是挂到队首,这样,接收者就能够优先接收到紧急消息,从而及时进行消息处理。 + +接收消息 +```c +rt_err_t rt_mq_recv(rt_mq_t mq,//消息队列对象的句柄 + void* buffer,// 消息内容 + rt_size_t size,// 消息大小 + rt_int32_t timeout);//指定的超时时间 +``` +当消息队列中有消息时,接收者才能接收消息,否则接收者会根据超时时间设置,或挂起在消息队列的等待线程队列上,或直接返回。接收消息时,接收者需指定存储消息的消息队列对象句柄,并且指定一个内存缓冲区,接收到的消息内容将被复制到该缓冲区里。此外,还需指定未能及时取到消息时的超时时间,接收一个消息后消息队列上的队首消息被转移到了空闲消息链表的尾部。 + +### 信号 +信号(又称为软中断信号),在软件层次上是对中断机制的一种模拟,在原理上,一个线程收到一个信号与处理器收到一个中断请求可以说是类似的。 + +信号本质是软中断,用来通知线程发生了异步事件,用做线程之间的异常通知、应急处理。一个线程不必通过任何操作来等待信号的到达,事实上,线程也不知道信号到底什么时候到达,线程之间可以互相通过调用`rt_thread_kill()`发送软中断信号。 + +收到信号的线程对各种信号有不同的处理方法,处理方法可以分为三类: +- 第一种是类似中断的处理程序,对于需要处理的信号,线程可以指定处理函数,由该函数来处理。 +- 第二种方法是,忽略某个信号,对该信号不做任何处理,就像未发生过一样。 +- 第三种方法是,对该信号的处理保留系统的默认值。 + +假设线程1需要对信号进行处理,首先线程1安装一个信号并解除阻塞,并在安装的同时设定了对信号的异常处理方式;然后其他线程可以给线程1发送信号,触发线程1对该信号的处理。 +#### 信号API +安装 +```c +rt_sighandler_t rt_signal_install(int signo,rt_sighandler_t[] handler); +``` +如果线程要处理某一信号,那么就要在线程中安装该信号。安装信号主要用来确定信号值及线程针对该信号值的动作之间的映射关系,即线程将要处理哪个信号,该信号被传递给线程时,将执行何种操作。handler参数,决定了该信号的不同的处理方法。参数指向当信号发生时用户自定义的处理函数,由该函数来处理(类似中断的方式)。 +参数设为`SIG_IGN`,忽略某个信号,对该信号不做任何处理。参数设为`SIG_DFL`,系统会调用默认的处理函数`_signal_default_handler()`。 + +阻塞信号与解除信号阻塞 +```c +void rt_signal_mask(int signo); + +void rt_signal_unmask(int signo); +``` +信号阻塞,也可以理解为屏蔽信号。如果该信号被阻塞,则该信号将不会递达给安装此信号的线程,也不会引发软中断处理。线程中可以安装好几个信号,使用此函数可以对其中一些信号给予“关注”,那么发送这些信号都会引发该线程的软中断。 + +发送信号 +```c +int rt_thread_kill(rt_thread_t tid, int sig); +``` +当需要进行异常处理时,可以给设定了处理异常的线程发送信号。 + +等待信号 +```c +int rt_signal_wait(const rt_sigset_t *set, + rt_siginfo_t[] *si, rt_int32_t timeout); +``` +等待set信号的到来,如果没有等到这个信号,则将线程挂起,直到等到这个信号或者等待时间超过指定的超时时间timeout。如果等到了该信号,则将指向该信号体的指针存入si。 \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/\347\254\2544\345\244\251\347\254\224\350\256\260.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/\347\254\2544\345\244\251\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..194779464bde165d87dbf74c9f7af053f8f4c903 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\346\235\234\346\265\251\347\204\266/\347\254\224\350\256\260/\347\254\2544\345\244\251\347\254\224\350\256\260.md" @@ -0,0 +1,338 @@ +## 驱动开发 + +### RT-Thread I/O设备框架 +不同厂家的MCU关于SPI接口的API设计可能是不同的,例如 +- GD: spi_i2s_data_transmit +- ST: HAL_SPI_Transmit +- NXP: LPSPI_MasterTransferBlocking +- LPC: SPI_MasterTransferBlocking + +那么更换主控芯片就需要重新开发代码,为了实现不同厂家针对同一个外设使用相同的API,将驱动代码与驱动设备代码分离。 +![结构1](figures/day4/structure1.png) + +RTT框架的演变如下所示 +![机构演变](figures/day4/structure2.png) + +驱动框架类图 +![类图](figures/day4/class_diagram.png) + +绝大部分的嵌入式系统都包括一些I/O设备,例如仪器上的数据显示屏、工业设备上的串口通信、数据采集设备上用于保存数据的Flash或SD卡,以及网络设备的以太网接口等,都是嵌入式系统中容易找到的I/O设备例子。 + +I/O提供一套接口open、write、read、control、close。SPI、I2C、GPIO、WDG提供特定的API。 +![顺序图](figures/day4/sequence_diagram.png) + +rt_device及其中的ops的定义如下 +```c +struct rt_device { + struct rt_object parent; /*内核对象基类*/ + enum rt_device_class_type type; /*设备类型*/ + rt_uint16_t flag; /*设备参数*/ + rt_uint16_t open_flag; /*设备打开标志*/ + rt_uint8_t ref_count; /*设备被引用次数*/ + rt_uint8_t device_id; /*设备ID, 0-255*/ + + /*数据收发回调函数*/ + rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size); + rt_err_t (*tx_complete)(rt_device_t dev, void *buffer); + + const struct_device_ops *ops; /*设备操作方法, 用于对接下一层*/ + + /*设备的私有数据*/ + void *user_data; +} +typedef struct rt_device *rt_device_t; + +struct rt_device_ops { +//common device interface +rt_err_t (*init) (rt_device_t dev); +rt_err_t (*open) (rt_device_t dev, rt_uint16_t oflag); +rt_err_t (*close) (rt_device_t dev); +rt_size_t (*read) (rt_device_t dev, rt_off_t pos,void *buffer,rt_size_t size); +rt_size_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer,rt_size_t size); +rt_err_t (*control) (rt_device_t dev, int cmd,void *args); +}; +``` + +RTT支持的I/O设备类型为,相较windows等类别更多。 +```c +RT_Device_Class_Char /*字符设备*/ +RT_Device_Class_Block /*块设备*/ +RT_Device_Class_NetIf /*网络接口设备*/ +RT_Device_Class_MTD /*内存设备*/ +RT_Device_Class_RTC /*RTC设备*/ +RT_Device_Class_Sound /*声音设备*/ +RT_Device_Class_Graphic /*图形设备*/ +RT_Device_Class_I2CBUS /*I2C总线设备*/ +RT_Device_Class_USBDevice /*USB device设备*/ +RT_Device_Class_USBHost /*USB host设备*/ +RT_Device_Class_SPIBUS /*SPI总线设备*/ +RT_Device_Class_SPIDevice /*SPI设备*/ +RT_Device_Class_SDIO /*SDI0设备*/ +RT_Device_Class_Miscellaneous /*杂类设备*/ +``` + +字符设备和块设备的特点与区别主要为是否可以被随机访问: +- 字符设备:提供连续的数据流,应用程序可以顺序读取,通常不支持随机存取。相反, +此类设备支持按字节/字符来读写数据。举例来说,键盘、串口、调制解调器都是典型 +的字符设备 +- 块设备:应用程序可以随机访问设备数据,程序可自行确定读取数据的位置。硬盘、 +软盘、CD-ROM驱动器和闪存都是典型的块设备,应用程序可以寻址磁盘上的任何位 +置,并由此读取数据。此外,数据的读写只能以块(通常是512B)的倍数进行。与字符 +设备不同,块设备并不支持基于字符的寻址。 + +不同的组件和应用会依赖不同的设备,对设备进行分类,可以做到对一类设备同样的控制。 + +### RT-Thread I/O API +创建与销毁设备 +```c +rt_device_t rt_device_create(int type, int attach_size); +void rt_device_destroy(rt_device_t device); +``` + +注册与注销设备及其flag,将设备注册到I/O管理层,就可以通过find函数找到句柄。 +```c +rt_err_t rt_device_register(rt_device_t dev, const char* name,rt_uint8_t flags); +rt_err_t rt_device_unregister(rt_device_t dev); + +#define RT_DEVICE_FLAG_RDONLY 0x001 /*只读*/ +#define RT_DEVICE_FLAG_WRONLY 0x002 /*只写*/ +#define RT_DEVICE_FLAG_RDWR 0x003 /*读写*/ +#define RT_DEVICE_FLAG_REMOVABLE 0x004 /*可移除*/ +#define RT_DEVICE_FLAG_STANDALONE 0x008 /*独立*/ +#define RT_DEVICE_FLAG_SUSPENDED 0x020 /*挂起*/ +#define RT_DEVICE_FLAG_STREAM 0x040 /*流模式*/ +#define RT_DEVICE_FLAG_INT_RX 0X100 /*中断接收*/ +#define RT_DEVICE_FLAG_DMA_RX 0X200 /*DMA接收*/ +#define RT_DEVICE_FLAG_INT_TX 0x400 /*中断发送*/ +#define RT_DEVICE_FLAG_DMA_TX 0x800 /*DMA发送*/ +``` + +应用程序通过I/O设备管理接口来访问硬件设备,设备驱动实现后,应用程序就可以访问该硬件。I/O设备管理接口和IO设备的操作方法的映射关系如图 +![访问设备](figures/day4/device_access.png) + +查找与初始化设备 +```c +rt_device_t rt_device_find(const char*name); +rt_err_t rt_device_init(rt_device_t dev); +``` + +打开与关闭设备,及打开的标志位 +```c +rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags); +rt_err_t rt_device_close(rt_device_t dev); + +#define RT_DEVICE_OFLAG_CLOSE 0x000 /*设备己经关闭(内部使用)*/ +#define RT_DEVICE_OFLAG_RDONLY 0x001 /*以只读方式打开设备*/ +#define RT_DEVICE_OFLAG_WRONLY 0x002 /*以只写方式打开设备*/ +#define RT_DEVICE_OFLAG_RDWR 0x003 /*以读写方式打开设备*/ +#define RT_DEVICE_OFLAG_OPEN 0x008 /*设备己经打开(内部使用)*/ +#define RT_DEVICE_FLAG_STREAM 0x040 /*设备以流模式打开*/ +#define RT_DEVICE_FLAG_INT_RX 0x100 /*设备以中断接收模式打开*/ +#define RT_DEVICE_FLAG_DMA_RX 0x200 /*设备以DMA接收模式打开*/ +#define RT_DEVICE_FLAG_INT_TX 0x400 /*设备以中断发送模式打开*/ +#define RT_DEVICE_FLAG_DMA_TX 0x800 /*设备以DMA发送模式打开*/ +``` +> 如果上层应用程序需要设置设备的接收回调函数,必须以`RT_DEVICE_FLAG_INT_RX`或`RT_DEVICE_FLAG_DMA_RX`的方式打开设备,否则不会回调函数。 + +控制设备及其控制参数 +```c +rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void *arg); + +#define RT_DEVICE_CTRL_RESUME 0x01 /*恢复设备*/ +#define RT_DEVICE_CTRL_SUSPEND 0x02 /*挂起设备*/ +#define RT_DEVICE_CTRL_CONFIG 0x03 /*配置设备*/ +#define RT_DEVICE_CTRL_SET_INT 0x10 /*设置中断*/ +#define RT_DEVICE_CTRL_CLR_INT 0x11 /*清中断*/ +#define RT_DEVICE_CTRL_GET_INT 0x12 /*获取中断状态*/ +``` + +读写操作 +```c +rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size); +rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos,const void* buffer, rt_size_t size); +``` + +数据接收回调,当硬件设备收到数据时,可以通过如下函数回调另一个函数来设置数据接收指示,通知上层应用线程有数据到达: +```c +rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev, rt_size_t size)); +``` + +在应用程序调用rt_device_write()写入数据时,如果底层硬件能够支持自动发送,那么上层应用可以设置一个回调函数。这个回调函数会在底层硬件数据发送完成后(例如DMA传送完成或FIFO己经写入完毕产生完成中断时)调用。可以通过如下函数设置设备发送完成指示,函数参数及返回值见: +```c +rt_err_t rt_device_set_tx_complete(rt_device_t dev, rt_err_t (*tx_done)(rt_device_t dev, void *buffer)); +``` + +I/O框架的调用关系图 +![IO框架调用关系](figures/day4/io_invoke_relationship.png) + + +### GPIO应用与驱动开发 +芯片上的引脚一般分为4类:电源、时钟、控制与I/O,I/O口在使用模式上又分为General Purpose Input Output(通用输入/输出),简称GPIO,与功能复用I/O(如SPI/I2C/UART等)。 + +大多数MCU的引脚都不止一个功能。不同引脚内部结构不一样,拥有的功能也不一样。可以通过不同的配置,切换引脚的实际功能。 + +通用I/O口主要特性为可编程控制中断:中断触发模式可配置,一般有下图所示5种中断触发模式,RTT只实现了其中的边沿触发模式 +![触发模式](figures/day4/trigger_mode.png) + +#### 应用开发常用接口 +应用程序通过RT-Thread提供的PIN设备管理接口来访问GPIO,相关接口如下: +```c +rt_pin_mode() //设置引脚模式 +rt_pin_write() //设置引脚电平 +rt_pin_read() //读取引脚电平 +rt_pin_attach_irg() //绑定引脚中断回调函数 +rt_pin_irq_enable() //使能引脚中断 +rt_pin_detach_irq() //脱离引脚中断回调函数 +``` + +引脚在使用前需要先设置好输入或者输出模式,通过如下函数及宏定义完成: +```c +void rt_pin_mode(rt_base_t pin, rt_base_t mode); +#define PIN_MODE_OUTPUT 0x00 /*输出*/ +#define PIN_MODE_INPUT 0x01 /*输入*/ +#define PIN_MODE_INPUT_PULLUP 0x02 /*上拉输入*/ +#define PIN_MODE_INPUT_PULLDOWN 0x03 /*下拉输入*/ +#define PIN_MODE_OUTPUT_OD 0x04 /*开漏输出*/ +``` + +设置引脚输出电平的函数及对应宏定义如下所示: +```c +void rt_pin_write(rt_base_t pin, rt_base_t value); +#define PIN_LOW 0x00 /*低电平*/ +#define PIN_HIGH 0x01 /*高电平*/ +``` + +读取引脚输入电平的函数及返回值的宏定义如下所示: +```c +void rt_pin_read(rt_base_t pin); +#define PIN_LOW 0x00 /*低电平*/ +#define PIN_HIGH 0x01 /*高电平*/ +``` + +### I2C从机应用与驱动开发 +I2C是Inter-Integrated Circuit的简称,读作:I-squared-C。由飞利浦公司于1980年代提出,为了让主板、嵌入式系统或手机用以连接低速周边外部设备而发展。 + +I2C总线数据传输格式 +![I2C传输协议](figures/day4/i2c_protocol.png) + +向从机写数据,开始数据传输后,先发送一个起始位(S),主设备发送一个地址数据(由7bt的从设备地址,和最低位的写标志位组成的8t字节数据,该读写标志位决定数据的传输方向),然后,主设备释放SDA线,并等待从设备的应答信号(ACK)。每一个字节数据的传输都要跟一个应答信号位。数据传输以停止位(P)结束,并且释放I2C总线。 +![i2c写数据](figures/day4/i2c_write.png) + +向从机读数据,开始通讯时,主设备先发送一个起始信号(S),主设备发送一个地址数据(由7bt的从设备地址,和最低位的写标志位组成的8bit字节数据),然后,主设备释放SDA线,并等待从设备的应答信号(ACK),从设备应答主设备后,主设备再发送要读取的寄存器地址,从设备应答主设备(ACK),主设备再次发送起始信号(S),主设备发送设备地址(包含读标志),从设备应答主设备,并将该寄存器的值发送给主设备; +![i2c读数据](figures/day4/i2c_read.png) + +I2C从机常用的模式有,向I2C从机设备某个寄存器写一个字节数据、多个字节数据;从I2C从机设备,某个寄存器读取一个字节数据、多个字节数据。 + +开启I2C驱动`Hard ware Drivers Config -> On-chip Peripheral Drivers -> Enable I2C`后,使用list_device命令查看总线设备注册情况。 + +I2C设备探测软件包`RT-Thread online packages -> peripheral libraries and drivers -> i2c-tools`可以探测总线上的设备。 + +#### API +查找设备:在使用I2C总线设备前需要根据I2C总线设备名称获取设备句柄,进而才可以操作I2C总线设备,查找设备函数如下所示: +```c +rt_device_t rt_device_find(const char*name); +``` + +一般情况下,注册到系统的I2C设备名称为i2c0,i2c1等,使用示例如下所示: +```c +struct rt_i2c_bus_device *i2c_bus;/*工2C总线设备句柄*/ +/*查找虹2C总线设备,获取T2C总线设备句柄*/ +i2c_bus (struct rt_i2c_bus_device *)rt_device_find(name); +``` + +数据传输 +```c +rt_size_t rt_i2c_transfer(struct rt_i2c_bus_device *bus, + struct rt_i2c_msg msgs[], + rt_uint32_t num); +struct rt_i2c_msg{//I2C消息数据结构原型 + rt_uint16_t addr;/*从机地址,支持7位和10位地址,需查看设备手册*/ + rt_uint16_t flags;/*读、写标志等,查看下方定义,可以与其他宏使用|组合*/ + rt_uint16_t len;/*读写数据字节数*/ + rt_uint8_t *buf;/*读写数据缓冲区指针*/ +} +``` +I2C设备接口使用的从机地址均不包含读写位,读写位控制需修改标志flags。 +```c +#define RT_I2C_WR 0x0000 /*写标志*/ +#define RT_I2C_RD (1u<0) /*读标志*/ +#define RT_I2C_ADDR_10BIT (1u<<2)/*10位地址模式*/ +#define RT_I2C_NO_START (1u<<4)/*无开始条件*/ +#define RT_I2C_IGNORE_NACK (1u<<5)/*忽视NACK*/ +#define RT_I2C_NO_READ_ACK (1u<<6)/*读的时候不发送ACK*/ +``` +> 此函数会调用rt_mutex_take(),不能在中断服务程序里面调用,会导致assertion报错。 + +使用思路为,查找I2C总线设备 -> 构造msgs消息 -> 启动transfer传输 -> 处理结果。 + +### SPI从机与应用开发 +SPI(Serial Peripheral Interface,串行外设接口)是一种高速、全双工、同步通信总线,常用于短距离通讯,主要应用于EEPROM、FLASH、实时时钟、AD转换器、还有数字信号处理器和数字信号解码器之间。SPI一般使用4根线通信,如下图所示: +![SPI原理](figures/day4/spi.png) + +在RT-Thread中,SPI设备分为“SPI总线”和“SPI设备”两大类,SPI总线对应SPI控制器,SPI设备对应不同CS连接的从设备,使用前需要先注册SPI总线,再把从设备挂载到总线上。 + +SPI开发模式为:注册SPI Device设备 -> 打开SPI Device设备 -> 使用SPI框架提供API编程发送接收数据 -> 关闭SPI Device设备 + +挂载SPI设备:SPI驱动会注册SPI总线,SPI设备需要挂载到已经注册好的SPI总线上,下面的函数用于挂载一个SPI设备到指定的SPI总线,并向内核注册SPI设备,并将user_data保存到SPI设备的控制块中。 +```c +rt_err_t rt_spi_bus_attach_device(struct rt_spi_device *device, + const char *name, + const char *bus_name, + void *user_data)//私有数据一般是片选的引脚 +``` + +一般SPI总线命名原则为spi,SPI设备命名原则为spixy,如spi10表示挂载在spi1总线上的0号设备。user data一般为SPI设备的CS引脚指针,进行数据传输时SPI控制器会操作此引脚进行片选。 + +控制SPI设备相关的API +```c +rt_device_find() /*根据SPI设备名称查找设备获取设备句柄*/ +rt_spi_configure() /*配置SPI设备*/ +rt_spi_transfer() /*传输一次数据,不需要手动片选,相当于调用rt_spi_transfer_message*/ +rt_spi_send() /*发送一次数据,忽略接收到的数据*/ +rt_spi_recv() /*只接受一次数据*/ +rt_spi_send_then_send() /*连续发送两个缓冲区的数据,中间片选不释放,忽略接收到的数据*/ +rt_spi_send_then_recv() /*先发送后接收,中间片选不释放*/ +rt_spi_transfer_message() /*自定义传输数据,开始发送数据时片选,返回时释放*/ +``` +> SP1数据传输相关接口会调用rt_mutex_take(),此函数不能在中断服务程序里面调用,会导致assertion报错。 + +查找SPI设备,在使用SPI设备前需要根据SPI设备名称获取设备句柄,进而才可以操作SPI设备,查找设备函数如下所示: +```c +rt_device_t rt_device_find(const char*name); +``` +一般情况下,注册到系统的SPI设备名称为spi10,使用示例如下所示: +```c +#define W250_SPI_DEVICE_NAME "qspi10"/*SPI设备名称*/ +struct rt_spi_device *spi_dev_w25q;/*SPI设备句柄*/ +/*查找sp1设备获取设备句柄*/ +spi_dev_w25q (struct rt_spi_device *)rt_device_find(W25Q_SPI_DEVICE_NAME); +``` + +SPI设备驱动配置及宏定义 +```c +rt_err_t rt_spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg) +struct rt_spi_configuration { + rt_uint8_t mode;/*模式*/ + rt_uint8_t data_width;/*数据宽度,可取8位、16位、32位*/ + rt_uint16_t reserved;/*保留*/ + rt_uint32_t max_hz;/*最大频率*/ +} +/*设型散据传龄顺序5B位在前还是LSB位在前*/ +#define RT_SPI_LSB (0<<2)/*bit[2]:0-LSB*/ +#define RT SPI MSB (1<<2)/*b1t[2]:1-MSB*/ + +/*设SPI的主从模式*/ +#define RT_SPI_MASTER (0<<3)/*SPI master device*/ +#define RT_SPI_SLAVE (1<<3)/*SPI slave device*/ + +/*设置时钟极性时钟相位*/ +#define RT_SPI_MODE_0 (0|0)/*CPOL=0, CPHA=0*/ +#define RT_SPI_MODE_1 (0|RT_SPI_CPHA)/*CPOL=0, CPHA=1*/ +#define RT_SPI_MODE_2 (RT_SPI_CPOL|0)/*CPOL=1, CPHA=0*/ +#define RT_SPI_MODE_3 (RT_SPI_CPOL|RT_SPI_CPHA)/*CPOL=1, CPHA=1*/ + +#define RT_SPI_CS_HIGH (1<<4)/*ChipseLect active high*/ +#define RT_SPI_NO_CS (1<<5)/*No chipseLect*/ +#define RT_SPI_3WIRE (1<<6)/*SI/SO pin shared*/ +#define RT_SPI_READY (1<<7)/*Slave pulls low to pause*/ +``` \ No newline at end of file