diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day2/Day2-thead.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day2/Day2-thead.c" new file mode 100644 index 0000000000000000000000000000000000000000..d644ad4b349379630159111cc8e76eac50a7ed24 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day2/Day2-thead.c" @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 SummerGift first version + * 2018-11-19 flybreak add stm32f407-atk-explorer bsp + */ + +#include +#include +#include + +void user_thread1(void){ + while(1){ + rt_kprintf("run in user1 thread\r\n"); + rt_thread_delay(5); + + } +} +void user_thread2(void){ + while(1){ + rt_kprintf("run in user2 thread\r\n"); + rt_thread_delay(5); + + } +} +void user_thread3(void){ + while(1){ + rt_kprintf("run in user3 thread\r\n"); + rt_thread_delay(5); + + } +} +rt_thread_t tid1 = RT_NULL; +rt_thread_t tid2 = RT_NULL; +rt_thread_t tid3 = RT_NULL; +int main(void) +{ + /* thread 1 pri:11 tick:50 */ + tid1 = rt_thread_create("user1", user_thread1, RT_NULL, 1024, 11, 50); + if (tid1 != RT_NULL){ + rt_thread_startup(tid1); + } + + /* thread 2 pri:11 tick:100 */ + tid2 = rt_thread_create("user2", user_thread2, RT_NULL, 1024, 11, 100); + if (tid2 != RT_NULL){ + rt_thread_startup(tid2); + } + + /* thread 3 pri:10 tick:100 */ + tid2 = rt_thread_create("user3", user_thread3, RT_NULL, 1024, 10, 100); + if (tid3 != RT_NULL){ + rt_thread_startup(tid3); + } + return RT_EOK; +} \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/event.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/event.c" new file mode 100644 index 0000000000000000000000000000000000000000..c8e09ca5cfa0e962822d8b9180cc0edf146fe4f8 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/event.c" @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 SummerGift first version + * 2018-11-19 flybreak add stm32f407-atk-explorer bsp + */ + +#include +#include +#include + + +/* + * thread1 接受事件1&2 成功后发送事件3|4给thread2 + */ + +#define EVENT1 (1 << 1) +#define EVENT2 (1 << 2) +#define EVENT3 (1 << 3) +#define EVENT4 (1 << 4) + +static rt_event_t event = RT_NULL; + +//rt_uint32_t num1 = 0, num2 = 0; + +static rt_thread_t tid1 = RT_NULL; +static void thread1_entry(void *parameter) +{ + rt_err_t status = RT_ERROR; + rt_uint32_t recved = 0; + + status = rt_event_recv(event, EVENT1|EVENT2, RT_EVENT_FLAG_AND, RT_WAITING_FOREVER, &recved); + + if(status == RT_EOK){ + rt_kprintf("th1 receive event1&2 , recved = %d\n",recved); + rt_kprintf("th1 send event 3 \n"); + rt_event_send(event, EVENT3); + rt_thread_mdelay(1000); + rt_kprintf("th1 send event 4 \n"); + rt_event_send(event, EVENT4); + + } + +// while (1) +// { +// +// +// } +} +static rt_thread_t tid2 = RT_NULL; +static void thread2_entry(void *parameter) +{ +// rt_err_t status; + rt_kprintf("th2 send event1\n"); + rt_event_send(event, EVENT1); + rt_thread_mdelay(500); + + rt_kprintf("th2 send event2\n"); + rt_event_send(event, EVENT2); + rt_thread_mdelay(500); + + rt_err_t status = RT_ERROR; + rt_uint32_t recved = 0; + + + while(1){ + status = rt_event_recv(event, EVENT3|EVENT4, RT_EVENT_FLAG_OR|RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &recved); + if(status == RT_EOK){ + rt_kprintf("th2 receive event3|4, recevd = %d\n",recved); + + } + } + rt_kprintf("break\n"); + + + +// while (1) +// { +// +// } +} + + +int main(void) +{ + event = rt_event_create("event",RT_IPC_FLAG_PRIO); + + + + + + tid1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 6); + rt_thread_startup(tid1); + + tid2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid2); + + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/mailbox.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/mailbox.c" new file mode 100644 index 0000000000000000000000000000000000000000..c7f8afaf53d7e1906aafee7233d6342c2f1ff79c --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/mailbox.c" @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 SummerGift first version + * 2018-11-19 flybreak add stm32f407-atk-explorer bsp + */ + +#include +#include +#include + + +/* + * thread2 向 thread1 发送mb,其中str3是urgent发送 + * thread1 会先读取到str3 + */ + +static rt_mailbox_t mb; +static char str1[] = "first mail"; +static char str2[] = "second mail"; +static char str3[] = "third mail"; + + +static rt_thread_t tid1 = RT_NULL; +static void thread1_entry(void *parameter) +{ + rt_uint8_t cnt = 0; + char* rec = NULL; + while(cnt < 3){ + if(RT_EOK == rt_mb_recv(mb, (rt_ubase_t *)&rec, RT_WAITING_FOREVER)){ + rt_kprintf("rec: %s\n",rec); + cnt++; + } + } + + + + +} + +static rt_thread_t tid2 = RT_NULL; +static void thread2_entry(void *parameter) +{ + rt_mb_send(mb,(rt_uint32_t)&str1); + rt_mb_send(mb,(rt_uint32_t)&str2); + rt_mb_urgent(mb,(rt_uint32_t)&str3); + + + + rt_thread_mdelay(500); + +} + + +int main(void) +{ + mb= rt_mb_create("mailbox", 4*3, RT_IPC_FLAG_PRIO); + + + + tid1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 6); + rt_thread_startup(tid1); + + tid2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid2); + + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/msgqueue.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/msgqueue.c" new file mode 100644 index 0000000000000000000000000000000000000000..247b08df714876f91b5c2ab4a5373a43174d3f52 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/msgqueue.c" @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 SummerGift first version + * 2018-11-19 flybreak add stm32f407-atk-explorer bsp + */ + +#include +#include +#include + + +/* + * thread2 发送 A~Z, thread1接收 + */ + +static rt_mq_t msq; + +static rt_thread_t tid1 = RT_NULL; +static void thread1_entry(void *parameter) +{ + rt_uint8_t num = 0; + char rec = '\0'; + while(rec < 'Z'){ + if (rt_mq_recv(msq, &rec, sizeof(rec), RT_WAITING_FOREVER) == RT_EOK){ + num++; + rt_kprintf("receive %c\n",rec); + } + rt_thread_mdelay(10); + } + +} + +static rt_thread_t tid2 = RT_NULL; +static void thread2_entry(void *parameter) +{ + static char buf = 'A'; + while(buf <= 'Z'){ + rt_mq_send(msq, &buf, 1); + rt_kprintf("sent %c\n",buf); + buf++; + + rt_thread_mdelay(10); + } +} + + +int main(void) +{ + msq = rt_mq_create("msq",sizeof(char),10,RT_IPC_FLAG_PRIO); + + + tid1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 6); + rt_thread_startup(tid1); + + tid2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid2); + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/mutex.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/mutex.c" new file mode 100644 index 0000000000000000000000000000000000000000..b0c846a54f64d6631dd742f555dd3b4889f50659 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/mutex.c" @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 SummerGift first version + * 2018-11-19 flybreak add stm32f407-atk-explorer bsp + */ + +#include +#include +#include + + +/* + * thread1 与 thread2 竞争同一资源。 + * 当thread1 持有资源时thread2 无法访问 + * num1 num2 使用互斥量加锁, num3未加锁 + * 通过输出可以看到,num1与num2一直相等,而num3存在与前两者不一致的情况 + * + */ +static rt_mutex_t mutex = RT_NULL; +rt_uint32_t num1 = 0, num2 = 0, num3 = 0; + +static rt_thread_t tid1 = RT_NULL; +static void thread1_entry(void *parameter) +{ + + while (num3 < 1000) + { + rt_mutex_take(mutex, 0); + rt_kprintf("thread 1 num1 = %d, num2 = %d, num3 = %d\n",num1,num2,num3); + + num1++; + num2++; + rt_kprintf("release mutex\n"); + rt_mutex_release(mutex); + num3++; + + } +} +static rt_thread_t tid2 = RT_NULL; +static void thread2_entry(void *parameter) +{ + rt_err_t status; + + while (num3 < 1000) + { + rt_mutex_take(mutex, 0); + rt_kprintf("thread 2 num1 = %d, num2 = %d, num3 = %d\n",num1,num2,num3); + num1++; + num2++; + rt_kprintf("release mutex\n"); + rt_mutex_release(mutex); + num3++; + } +} + + +int main(void) +{ + mutex = rt_mutex_create("mutex1",RT_IPC_FLAG_PRIO); + + + + + + tid1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 6); + rt_thread_startup(tid1); + + tid2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid2); + + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/semaphore.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/semaphore.c" new file mode 100644 index 0000000000000000000000000000000000000000..4bf0d5e5e1e64ed5ad2bdb03f74a64b0830409a0 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/semaphore.c" @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 SummerGift first version + * 2018-11-19 flybreak add stm32f407-atk-explorer bsp + */ + +#include +#include +#include + + +/* + * thread1 定时释放sem + * thread2 take sem 如果take到就计数++ + * thread1优先级 == thread2,二者轮询调度,thread2可能会获取信号量失败。 + * + */ +static rt_sem_t dynamic_sem = RT_NULL; + + +static rt_thread_t tid1 = RT_NULL; +static void thread1_entry(void *parameter) +{ + rt_uint32_t count = 0; + while (1) + { + count++; + count %= 100; + if(count ==0){ + rt_kprintf("release sem, count = %d\n",count); + rt_sem_release(dynamic_sem); +// rt_thread_mdelay(5); + } + + } +} +static rt_thread_t tid2 = RT_NULL; +static void thread2_entry(void *parameter) +{ + rt_uint32_t take_num = 0; + rt_err_t status; + + while (1) + { + rt_kprintf("taking sem..."); + + status = rt_sem_take(dynamic_sem, RT_WAITING_FOREVER); + if(status == RT_EOK){ + take_num++; + rt_kprintf("succsessful, take_num = %d\n",take_num); + } + } +} + + +int main(void) +{ + dynamic_sem = rt_sem_create("sema", 0, RT_IPC_FLAG_PRIO); + + + + tid1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid1); + + tid2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid2); + + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/signal.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/signal.c" new file mode 100644 index 0000000000000000000000000000000000000000..07cff1d12ff14447333103bbf505fed973835bfc --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day3/signal.c" @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 SummerGift first version + * 2018-11-19 flybreak add stm32f407-atk-explorer bsp + */ + +#include +#include +#include + + +/* + * thread1 和 thread2 分别安装信号,main进行发送,当num>2000后 + * thread2将mask信号,只有thread1还能接收到信号 + */ +rt_uint32_t num = 1000; +void signal_handler1(int sig) +{ + + rt_kprintf("thread1 received signal %d\n", sig); + num += 2; + + rt_kprintf("num = %d\n",num); +} +void signal_handler2(int sig) +{ + rt_kprintf("thread2 received signal %d\n", sig); + num += 2; + + rt_kprintf("num = %d\n",num); +} + +static rt_thread_t tid1 = RT_NULL; +static void thread1_entry(void *parameter) +{ + rt_signal_install(SIGUSR1, signal_handler1); + rt_signal_unmask(SIGUSR1); + + rt_uint8_t local = 0; + while(1){ + local++; + rt_thread_mdelay(100); + + } + + +} + +static rt_thread_t tid2 = RT_NULL; +static void thread2_entry(void *parameter) +{ + rt_signal_install(SIGUSR2, signal_handler2); + rt_signal_unmask(SIGUSR2); + + + + rt_uint8_t local = 0; + while(1){ + if(num > 2000)rt_signal_mask(SIGUSR2); + local++; + rt_thread_mdelay(100); + + } + +} + + +int main(void) +{ + + + tid1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 6); + rt_thread_startup(tid1); + + tid2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY + 1, 5); + rt_thread_startup(tid2); + + while(1){ + rt_thread_kill(tid1, SIGUSR1); + rt_thread_kill(tid2, SIGUSR2); + rt_thread_mdelay(10); + } + + + + + return RT_EOK; +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day4/dev_pwm.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day4/dev_pwm.c" new file mode 100644 index 0000000000000000000000000000000000000000..44c2d87eae32b892c3ccb9c2b8e34bb80ed0be31 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day4/dev_pwm.c" @@ -0,0 +1,33 @@ +// +// Created by winston on 25-8-1. +// +// #ifdef RT_USING_DEV_PWM + +#include +#include +#include + +rt_err_t rt_hw_dev_pwm_register(rt_dev_pwm_device_t dev_pwm, const char *name, const struct rt_dev_pwm_ops *ops, const void *user_data) +{ + // RT_ASSERT(ops != RT_NULL && ops->convert != RT_NULL); + rt_err_t result; + + dev_pwm->ops = ops; + + result = rt_device_register(&dev_pwm->parent, name, RT_DEVICE_FLAG_RDWR); + + return result; +} + +rt_uint32_t rt_dev_pwm_read(rt_dev_pwm_device_t dev) +{ + static rt_uint32_t value; + dev->ops->read(dev, &value); + return value; +} +void rt_dev_pwm_write(rt_dev_pwm_device_t dev) +{ + dev->ops->write(dev); +} + +// #endif \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day4/dev_pwm.h" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day4/dev_pwm.h" new file mode 100644 index 0000000000000000000000000000000000000000..4289ac2ed4509de88374ede0d8059bcdefabdf0f --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day4/dev_pwm.h" @@ -0,0 +1,31 @@ +// +// Created by winston on 25-8-1. +// + +#ifndef DEV_PWM_H +#define DEV_PWM_H + +#include + +struct rt_dev_pwm_ops +{ + void (*read)(struct rt_dev_pwm_device *dev, rt_uint32_t *val); + void (*write)(struct rt_dev_pwm_device *dev); +}; +/** + * @brief adc device + */ +struct rt_dev_pwm_device +{ + struct rt_device parent; + const struct rt_dev_pwm_ops *ops; +}; +typedef struct rt_dev_pwm_device *rt_dev_pwm_device_t; + +rt_err_t rt_hw_dev_pwm_register(rt_dev_pwm_device_t dev_pwm, const char *name, const struct rt_dev_pwm_ops *ops, const void *user_data); + +rt_uint32_t rt_dev_pwm_read(rt_dev_pwm_device_t dev); + +void rt_dev_pwm_write(rt_dev_pwm_device_t dev); + +#endif // DEV_PWM_H diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day4/drv_dev_pwm.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day4/drv_dev_pwm.c" new file mode 100644 index 0000000000000000000000000000000000000000..d5a24f455ed1e2a70a3ab206f534cd7b225fbd12 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day4/drv_dev_pwm.c" @@ -0,0 +1,31 @@ +// +// Created by winston on 25-8-1. +// +#include +#include +#include + +static struct dev_pwm dev; + +void dev_pwm_write(struct rt_device *device) +{ + dev.count++; +} +void dev_pwm_read(struct rt_device *device, rt_uint32_t *val) +{ + *val = dev.count; +} + +struct rt_dev_pwm_ops ops = + { + dev_pwm_read, + dev_pwm_write, + +}; + +static int dev_pwm_init(void) +{ + dev.count = 0; + rt_hw_dev_pwm_register(&dev.parent, "dev_pwm", &ops, NULL); +} +INIT_APP_EXPORT(dev_pwm_init); diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day4/drv_dev_pwm.h" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day4/drv_dev_pwm.h" new file mode 100644 index 0000000000000000000000000000000000000000..afffb83cdb7302e9d46487c3eefef47ccdb5abff --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day4/drv_dev_pwm.h" @@ -0,0 +1,19 @@ +// +// Created by winston on 25-8-1. +// + +#ifndef DRV_DEV_PWM_H +#define DRV_DEV_PWM_H + +#include "rttypes.h" + +struct dev_pwm +{ + struct rt_dev_pwm_device parent; + rt_uint32_t count; +}; + +void dev_pwm_write(struct rt_device *device); +void dev_pwm_read(struct rt_device *device, rt_uint32_t *val); + +#endif // DRV_DEV_PWM_H diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day4/main.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day4/main.c" new file mode 100644 index 0000000000000000000000000000000000000000..c7e252075b1eaa3d68668919e34c64c78833c087 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day4/main.c" @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-03-08 obito0 first version + * 2023-12-03 Meco Man support nano version + */ + +#include +#include + +#include +#ifndef RT_USING_NANO +#include +#endif /* RT_USING_NANO */ + +#include + +int main(void) +{ + static rt_uint32_t value; + rt_err_t ret = RT_EOK; + rt_device_t dev = RT_NULL; + + // step 1: find the device by name + dev = rt_device_find("dev_pwm"); + if (dev == RT_NULL) + { + rt_kprintf("find %s device fail!\n", "dev_pwm"); + } + else + { + // step 2: open the device by device hendle + ret = rt_device_open(dev, 0); + if (ret == RT_EOK) + { + rt_kprintf("open %s device success!\n", "dev_pwm"); + } + } + // TODO: if the device find error, the while will Unknown operation + while (1) + { + value = rt_dev_pwm_read(dev); + rt_dev_pwm_write(dev); + rt_kprintf("the value is :%d \n", value); + + rt_thread_mdelay(500); + } +} diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day5/README.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day5/README.md" new file mode 100644 index 0000000000000000000000000000000000000000..7aaa5976511700844332a137a79d38d858fdc20f --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day5/README.md" @@ -0,0 +1,10 @@ +## 说明 +使用库: +1. kawaii-mqtt(latest) +2. RyanJson(latest) + + +具体功能实现: + + +查看test.c diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day5/test.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day5/test.c" new file mode 100644 index 0000000000000000000000000000000000000000..1be809ac601f9a9f89b45393f58c932d380972ba --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day5/test.c" @@ -0,0 +1,123 @@ + +/* + * @Author: jiejie + * @Github: https://github.com/jiejieTop + * @LastEditTime: 2020-06-17 14:35:29 + * @Description: the code belongs to jiejie, please keep the author information and source code according to the license. + */ +#include +#include +#include +#include +#include +#include "mqttclient.h" + +#include "RyanJson.h" + +#ifndef KAWAII_MQTT_HOST +#define KAWAII_MQTT_HOST "broker.emqx.io" +#endif +#ifndef KAWAII_MQTT_PORT +#define KAWAII_MQTT_PORT "1883" +#endif +#ifndef KAWAII_MQTT_CLIENTID +#define KAWAII_MQTT_CLIENTID "random_client_rx115579" +#endif +#ifndef KAWAII_MQTT_USERNAME +#define KAWAII_MQTT_USERNAME "rtt" +#endif +#ifndef KAWAII_MQTT_PASSWORD +#define KAWAII_MQTT_PASSWORD "rtt" +#endif +#ifndef KAWAII_MQTT_SUBTOPIC +#define KAWAII_MQTT_SUBTOPIC "rtt-sub" +#endif +#ifndef KAWAII_MQTT_PUBTOPIC +#define KAWAII_MQTT_PUBTOPIC "rtt-pub" +#endif + +static char mqtt_payload[256]; +static RyanJson_t payload; + +static void sub_topic_handle1(void *client, message_data_t *msg) +{ + (void)client; + KAWAII_MQTT_LOG_I("-----------------------------------------------------------------------------------"); + KAWAII_MQTT_LOG_I("%s:%d %s()...\ntopic: %s\nmessage:%s", __FILE__, __LINE__, __FUNCTION__, msg->topic_name, (char *)msg->message->payload); + KAWAII_MQTT_LOG_I("-----------------------------------------------------------------------------------"); +} + +static int mqtt_publish_handle1(mqtt_client_t *client) +{ + #define STUDENT_NAME "ZiwenQu" + #define LEARNED_WHAT "I have learned how to use MQTT client in RT + mqtt_message_t msg; + memset(&msg, 0, sizeof(msg)); + + msg.qos = QOS0; + payload = RyanJsonCreateObject(); + + RyanJsonAddStringToObject(payload, "name", STUDENT_NAME); + RyanJsonAddStringToObject(payload, "study", LEARNED_WHAT); + + uint32_t len = 0; + char *json_str = RyanJsonPrint(payload, 0, RyanJsonFalse, &len); + if (!json_str) + return -1; + + strncpy(mqtt_payload, json_str, sizeof(mqtt_payload) - 1); + mqtt_payload[sizeof(mqtt_payload) - 1] = '\0'; + + RyanJsonFree(json_str); + RyanJsonDelete(payload); + + msg.payload = mqtt_payload; + msg.payloadlen = strlen(mqtt_payload); + + return mqtt_publish(client, KAWAII_MQTT_PUBTOPIC, &msg); +} + +static void kawaii_mqtt_demo(void *parameter) +{ + mqtt_client_t *client = NULL; + + mqtt_log_init(); + + client = mqtt_lease(); + + mqtt_set_host(client, KAWAII_MQTT_HOST); + mqtt_set_port(client, KAWAII_MQTT_PORT); + mqtt_set_user_name(client, KAWAII_MQTT_USERNAME); + mqtt_set_password(client, KAWAII_MQTT_PASSWORD); + mqtt_set_client_id(client, KAWAII_MQTT_CLIENTID); + mqtt_set_clean_session(client, 1); + + KAWAII_MQTT_LOG_I("The ID of the Kawaii client is: %s ", KAWAII_MQTT_CLIENTID); + + mqtt_connect(client); + + mqtt_subscribe(client, KAWAII_MQTT_SUBTOPIC, QOS0, sub_topic_handle1); + + while (1) + { + mqtt_publish_handle1(client); + + mqtt_sleep_ms(4 * 1000); + } +} + +int ka_mqtt(void) +{ + rt_thread_t tid_mqtt; + + tid_mqtt = rt_thread_create("kawaii_demo", kawaii_mqtt_demo, RT_NULL, 2048, 17, 10); + if (tid_mqtt == RT_NULL) + { + return -RT_ERROR; + } + + rt_thread_startup(tid_mqtt); + + return RT_EOK; +} +MSH_CMD_EXPORT(ka_mqtt, Kawaii MQTT client test program); diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day1-Init.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day1-Init.md" new file mode 100644 index 0000000000000000000000000000000000000000..aa6cbdd16dc0cb10140a0dee96fd614e61716771 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day1-Init.md" @@ -0,0 +1,42 @@ +第一次上传笔记 +# 第一天笔记 +跟晶体矿一样提交过bsp +## env的使用 +基本上没什么特殊的地方, 就是注意下怎么舒服的使用就好了 + +`pkgs --upgrade` 升级软件包 + +`pkgs --update` 更新软件包 + +`scons` 对文件进行编译 + +`menuconfig` 打开配置菜单,记得保存后再退出 + + +## 关于scons工具 +### sconscript语法 +创建新文件夹需要添加sconscript文件,每个文件夹都有一个,python通过该文件递归遍历每一个文件夹以控制编译内容.没有sconscript的话编译器是找不到文件的.新建文件夹的时候顺便复制一下同级目录下的对应的sconscript文件就行, 这样本目录下的对应文件就会添加到编译中去了. 如果感觉少了点什么东西, 可以看下sconscript文件里面是否有对应的后缀名. + + +## git +git是一个版本管理工具,组成为 工作区--暂存区--本地仓库--远端仓库 + +### git重要命令 +`git push/pull` 推送/拉取 + +`git add .` 添加所有修改的文件到暂存区 + +`git commit -m "log"` commit,log是commit的标题 + +`git log` 查看修改日志 + +`git status` 查看文件状态 + +`git checkout -b first_branch `创建一个分支名为first_branch,可以通过`git switch`切换分支,`git branch` 查看分支 + +`git reset --hard HEAD~` 硬重置,强制删除上一个commit + +`git reset --soft HEAD~` 软重置,把上一个commit退回暂存区里,后面还可以重新commit + +### 提示 +新手还是使用图形化界面比较好,直接上命令有点难了, 推荐sourcetree, 只要不rebase我觉得功能都够用. \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day2-SystemCore.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day2-SystemCore.md" new file mode 100644 index 0000000000000000000000000000000000000000..1dba4afad6f663c1a9bf113e11366436deb1dc33 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day2-SystemCore.md" @@ -0,0 +1,398 @@ +# 第二天笔记 +今天主要介绍了IDE的安装,如何在IDE上新建工程等.还有RTT整体初始化,堆栈/Systick/线程等初始化操作,我挑一些比较有意思的记录一下. + +## RT-Thread 的初始化 +根据系统初始化流程,以及对应的编译器选择. 针对GCC编译器的整体初始化方法如下. + +1. startup_stm32f407xx.s +``` c + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + ldr sp, =_estack /* set stack pointer */ + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss + +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call static constructors */ +/* bl __libc_init_array */ +/* Call the application's entry point.*/ + bl entry + bx lr +.size Reset_Handler, .-Reset_Handler +``` +ARM Cortex-M处理器的复位入口程序,芯片复位后初始化堆栈、数据段、BSS段,并调用系统初始化函数以及应用程序的入口点。进入`entry`函数获取RTT系统初始化. + +2. components.c + +``` c +#elif defined(__GNUC__) +/* Add -eentry to arm-none-eabi-gcc argument */ +int entry(void) +{ + rtthread_startup(); + return 0; +} +#endif +``` +根据对应的编译器初始化不同的函数名. 殊途同归最终都是调用`rtthread_startup()`函数进行系统的初始化. + +在`.s`文件中其实隐含了一个系统时钟初始化, 复位了时钟为内部HSI/复位了中断.切换到RTT之后的首要任务是进行时钟、中断的重新设置,并且尽快的初始化系统的TimeBase用于tick的计算. + +``` c +/** + * @brief This function will call all levels of initialization functions to complete + * the initialization of the system, and finally start the scheduler. + */ +int rtthread_startup(void) +{ + rt_hw_interrupt_disable(); + + /* board level initialization + * NOTE: please initialize heap inside board initialization. + */ + rt_hw_board_init(); + + /* show RT-Thread version */ + rt_show_version(); + + /* timer system initialization */ + rt_system_timer_init(); + + /* scheduler system initialization */ + rt_system_scheduler_init(); + +#ifdef RT_USING_SIGNALS + /* signal system initialization */ + rt_system_signal_init(); +#endif /* RT_USING_SIGNALS */ + + /* create init_thread */ + rt_application_init(); + + /* timer thread initialization */ + rt_system_timer_thread_init(); + + /* idle thread initialization */ + rt_thread_idle_init(); + +#ifdef RT_USING_SMP + rt_hw_spin_lock(&_cpus_lock); +#endif /* RT_USING_SMP */ + + /* start scheduler */ + rt_system_scheduler_start(); + + /* never reach here */ + return 0; +} +#endif /* RT_USING_USER_MAIN */ +``` + +在上述的函数中,从底层到系统/应用层逐步进行初始化,本节聚焦与`rt_hw_board_init()`,看下内部到底做了那些操作. + +3. drv_common.c +``` c +/** + * This function will initial STM32 board. + */ +RT_WEAK void rt_hw_board_init() +{ +#ifdef SCB_EnableICache + /* Enable I-Cache---------------------------------------------------------*/ + SCB_EnableICache(); +#endif + +#ifdef SCB_EnableDCache + /* Enable D-Cache---------------------------------------------------------*/ + SCB_EnableDCache(); +#endif + + /* HAL_Init() function is called at the beginning of the program */ + HAL_Init(); + + /* enable interrupt */ + __set_PRIMASK(0); + /* System clock initialization */ + SystemClock_Config(); + /* disable interrupt */ + __set_PRIMASK(1); + + rt_hw_systick_init(); + + /* Heap initialization */ +#if defined(RT_USING_HEAP) + rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END); +#endif + + /* Pin driver initialization is open by default */ +#ifdef RT_USING_PIN + rt_hw_pin_init(); +#endif + + /* USART driver initialization is open by default */ +#ifdef RT_USING_SERIAL + rt_hw_usart_init(); +#endif + + /* Set the shell console output device */ +#ifdef RT_USING_CONSOLE + rt_console_set_device(RT_CONSOLE_DEVICE_NAME); +#endif + + /* Board underlying hardware initialization */ +#ifdef RT_USING_COMPONENTS_INIT + rt_components_board_init(); +#endif +} +``` + +本节函数非常清晰,本质上是对上电后整个环境进行逐个初始化. 若存在Cache, 先I/D Cache进行处理. 之后则进行HAL层/Clock/Systick/Heap进行初始化. 之后则是对基础外设进行初始化. + +分节来逐步讲解: + +``` c +HAL_StatusTypeDef HAL_Init(void) +{ + /* Configure Flash prefetch, Instruction cache, Data cache */ +#if (INSTRUCTION_CACHE_ENABLE != 0U) + __HAL_FLASH_INSTRUCTION_CACHE_ENABLE(); +#endif /* INSTRUCTION_CACHE_ENABLE */ + +#if (DATA_CACHE_ENABLE != 0U) + __HAL_FLASH_DATA_CACHE_ENABLE(); +#endif /* DATA_CACHE_ENABLE */ + +#if (PREFETCH_ENABLE != 0U) + __HAL_FLASH_PREFETCH_BUFFER_ENABLE(); +#endif /* PREFETCH_ENABLE */ + + /* Set Interrupt Group Priority */ + HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); + + /* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */ + HAL_InitTick(TICK_INT_PRIORITY); + + /* Init the low level hardware */ + HAL_MspInit(); + + /* Return function status */ + return HAL_OK; +} +``` + +自动生成的代码,初始化默认中断分组;初始化Systick中断优先级; + +``` c +void SystemClock_Config(void) +{ + RCC_OscInitTypeDef RCC_OscInitStruct = {0}; + RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; + RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; + + /**Configure the main internal regulator output voltage + */ + __HAL_RCC_PWR_CLK_ENABLE(); + __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); + /**Initializes the CPU, AHB and APB busses clocks + */ + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE + |RCC_OSCILLATORTYPE_LSE; + RCC_OscInitStruct.HSEState = RCC_HSE_ON; + RCC_OscInitStruct.LSEState = RCC_LSE_ON; + RCC_OscInitStruct.LSIState = RCC_LSI_ON; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + RCC_OscInitStruct.PLL.PLLM = 4; + RCC_OscInitStruct.PLL.PLLN = 168; + RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; + RCC_OscInitStruct.PLL.PLLQ = 7; + if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) + { + Error_Handler(); + } + /**Initializes the CPU, AHB and APB busses clocks + */ + RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK + |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; + + if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) + { + Error_Handler(); + } + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; + PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) + { + Error_Handler(); + } +} +``` + +初始化对应的时钟源为选择的时钟源,获取最大性能. + +``` c +/* SysTick configuration */ +void rt_hw_systick_init(void) +{ +#if defined (SOC_SERIES_STM32H7) + HAL_SYSTICK_Config((HAL_RCCEx_GetD1SysClockFreq()) / RT_TICK_PER_SECOND); +#elif defined (SOC_SERIES_STM32MP1) + HAL_SYSTICK_Config(HAL_RCC_GetSystemCoreClockFreq() / RT_TICK_PER_SECOND); +#else + HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / RT_TICK_PER_SECOND); +#endif +#if !defined (SOC_SERIES_STM32MP1) + HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); +#endif + NVIC_SetPriority(SysTick_IRQn, 0xFF); +} +``` + +设置对应的systick,每秒产生对应`RT_TICK_PER_SECOND`指定的中断次数. + +```c +/** + * @brief This function will init system heap. + * + * @param begin_addr the beginning address of system page. + * + * @param end_addr the end address of system page. + */ +RT_WEAK void rt_system_heap_init(void *begin_addr, void *end_addr) +{ + rt_ubase_t begin_align = RT_ALIGN((rt_ubase_t)begin_addr, RT_ALIGN_SIZE); + rt_ubase_t end_align = RT_ALIGN_DOWN((rt_ubase_t)end_addr, RT_ALIGN_SIZE); + + RT_ASSERT(end_align > begin_align); + + /* Initialize system memory heap */ + _MEM_INIT("heap", begin_addr, end_align - begin_align); + /* Initialize multi thread contention lock */ + _heap_lock_init(); +} + +// 内存对齐的额外变量 (board.h) +extern int __bss_end; +#define HEAP_BEGIN ((void *)&__bss_end) +#endif + +#define HEAP_END STM32_SRAM_END +``` + +本处进行Heap的初始化,这里还是挺有意思的,这里将RAM除了`data`和`bss`区之外的所有空间都利用上了, 将上述空间做了4字节对齐后直接作为指定的Heap区. 不展开, 具体可以看`.map`文件和`__bss_end`. + +到此为止就完成了时钟和heap的初始化, 完成了最小内核的初始化. + +## 线程的初始化 +这也是比较有意思的第二点, 如何进行线程的初始化呢? + +无论是什么操作系统,本质上都是切换前保存对应的寄存器组, 切换对应的`PSP`和恢复寄存器组. + +``` c +struct exception_stack_frame +{ + rt_uint32_t r0; + rt_uint32_t r1; + rt_uint32_t r2; + rt_uint32_t r3; + rt_uint32_t r12; + rt_uint32_t lr; + rt_uint32_t pc; + rt_uint32_t psr; +}; + +struct stack_frame +{ +#if USE_FPU + rt_uint32_t flag; +#endif /* USE_FPU */ + + /* r4 ~ r11 register */ + rt_uint32_t r4; + rt_uint32_t r5; + rt_uint32_t r6; + rt_uint32_t r7; + rt_uint32_t r8; + rt_uint32_t r9; + rt_uint32_t r10; + rt_uint32_t r11; + + struct exception_stack_frame exception_stack_frame; +}; + +rt_uint8_t *rt_hw_stack_init(void *tentry, + void *parameter, + rt_uint8_t *stack_addr, + void *texit) +{ + struct stack_frame *stack_frame; + rt_uint8_t *stk; + unsigned long i; + + stk = stack_addr + sizeof(rt_uint32_t); + stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8); + stk -= sizeof(struct stack_frame); + + stack_frame = (struct stack_frame *)stk; + + /* init all register */ + for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++) + { + ((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef; + } + + stack_frame->exception_stack_frame.r0 = (unsigned long)parameter; /* r0 : argument */ + stack_frame->exception_stack_frame.r1 = 0; /* r1 */ + stack_frame->exception_stack_frame.r2 = 0; /* r2 */ + stack_frame->exception_stack_frame.r3 = 0; /* r3 */ + stack_frame->exception_stack_frame.r12 = 0; /* r12 */ + stack_frame->exception_stack_frame.lr = (unsigned long)texit; /* lr */ + stack_frame->exception_stack_frame.pc = (unsigned long)tentry; /* entry point, pc */ + stack_frame->exception_stack_frame.psr = 0x01000000L; /* PSR */ + +#if USE_FPU + stack_frame->flag = 0; +#endif /* USE_FPU */ + + /* return task's current stack address */ + return stk; +} +``` + +在上述代码中, Cortex-M响应中断时处理器硬件会将当前运行部分的上下文寄存器自动压入中断栈中,这部分的寄存器包括 PSR、PC、LR、R12、R3-R0 寄存器.`exception_stack_frame`刚好包含了上述寄存器,并且压栈顺序与对应结构体定义相同. 其他寄存器则需要手动进行保存. + + +本章节剩余内容,在学习完调度器后我补上. \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day3-Signal.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day3-Signal.md" new file mode 100644 index 0000000000000000000000000000000000000000..01df635091e3882ecb46eed7b9ead569fe9701ee --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day3-Signal.md" @@ -0,0 +1,175 @@ +# 线程间同步 + +## 信号量 +信号量多重多样,实际使用的时候一般用到三种. +* 互斥信号量: 主要用于线程之间同步数据,same as lock.某一个线程操作完之后才会释放,释放完毕之后才会获取对应的资源. +* 二值信号量: 主要用于线程与线程之间同步,一般理解为是Flag的升级版, 不需要自动查询和调度的FLAG. +* 计数信号量: 一般用于统计资源调配次数, 生产者-消费者模型必备的信号量. + +* 锁:单一的锁常应用于多个线程间对同一共享资源(即临界区)的访问。信号量在作为锁来使用时,通常应将信号量资源实例初始化成 1,代表系统默认有一个资源可用,因为信号量的值始终在 1 和 0 之间变动,所以这类锁也叫做二值信号量。如下图所示,当线程需要访问共享资源时,它需要先获得这个资源锁。当这个线程成功获得资源锁时,其他打算访问共享资源的线程会由于获取不到资源而挂起,这是因为其他线程在试图获取这个锁时,这个锁已经被锁上(信号量值是 0)。当获得信号量的线程处理完毕,退出临界区时,它将会释放信号量并把锁解开,而挂起在锁上的第一个等待线程将被唤醒从而获得临界区的访问权。 + + +### 信号量API +`rt_sem_create`: 动态创建信号量,当调用这个函数时,系统将先从对象管理器中分配一个semaphore 对象,并初始化这个对象,然后初始化父类IPC对象以及与 semaphore相关的部分。在创建信号量指定的参数中,信号量标志参数决定了当信号量不可用时,多个线程等待的排队方式。当选择`RT_IPC_FLAG_FIFO`(先进先出)方式时,那么等待线程队列将按照先进先出的方式排队,先进入的线程将先获得等待的信号量;当选择 `RT_IPC_FLAG_PRI0`(优先级等待)方式时,等待线程队列将按照优先级进行排队,优先级高的等待线程将先获得等待的信号量。 + +`rt_sem_delete`: 对应`rt_sem_create`。 +当调用这个函数时,系统将删除这个信号量。如果删除该信号量时,有线程正在等待该信号量,那么删除操作会先唤醒等待在该信号量上的线程(等待线程的返回值是-RTERROR),然后再释放信号量的内存资源。 + +`rt_sem_init`: 静态创建信号量 + +`rt_sem_detach`: 对应`rt_sem_init`。脱离信号量就是让信号量对象从内核对象管理器中脱离,适用于静态初始化的信号量。 +使用该函数后,内核先唤醒所有挂在该信号量等待队列上的线程,然后将该信号量从内核对象管理器中脱离。原来挂起在信号量上的等待线程将获得-RTERROR 的返回值。 + +`rt_sem_take`: 线程通过获取信号量来获得信号量资源实例,当信号量值大于零时,线程将获得信号量,并且相应的信号量值会减1。 +在调用这个函数时,如果信号量的值等于零,那么说明当前信号量资源实例不可用,申请该信号量的线程将根据 time 参数的情况选择直接返回、或挂起等待一段时间、或永久等待,直到其他线程或中断释放该信号量。如果在参数 time 指定的时间内依然得不到信号量,线程将超时返回,返回值是`- RT_ETIMEOUT` + +`rt_sem_trytake`: 等效rt_sem_take(sem,0);不等待资源,如果获取不到直接返回`-RT_ETIMEOUT` + +`rt_sem_release`: 释放信号量,唤醒等待的线程。 + +## 互斥量 +互斥量具有所有权的概念 + +互斥量是一种特殊的二值信号量。它和信号量不同的是,它支持: + +* 互斥量所有权:互斥量具有线程所有权,只有加锁的线程才能解锁,否则可能导致未定义行为(如死锁) +* 递归访问 +* 优先级继承:防止优先级反转 + +优先级反转:当一个高优先级线程试图通过信号量机制访问共享资源时,如果该信号量已被一低优先级线程持有,而这个低优先级线程在运行过程中可能又被其它一些中等优先级的线程抢占,因此造成高优先级线程被许多具有较低优先级的线程阻塞,实时性难以得到保证 + +优先级继承:提高某个占有某种资源的低优先级线程的优先级,使之与所有等待该资源的线程中优先级最高的那个线程的优先级相等,然后执行,而当这个低优先级线程释放该资源时,优先级重新回到初始设定。因此,继承优先级的线程避免了系统资源被任何中间优先级的线程抢占。 +### 互斥量API +`rt_mutex_creat`:动态创建互斥量,参数flag已经作废,无论用户选择 `RT_IPC_FLAG_PRIO` 还是 `RT_IPC_FLAG_FIFO`,内核均按照 `RT_IPC_FLAG_PRIO` 处理 + +`rt_mutex_delete`: 删除互斥量以释放系统资源,适用于动态创建的互斥量 + +`rt_mutex_init`: 静态创建互斥量 + +`rt_mutex_detach`: 把互斥量对象从内核对象管理器中脱离,适用于静态初始化的互斥量 + +`rt_mutex_take`:线程获取了互斥量,那么线程就有了对该互斥量的所有权,某一个时刻一个互斥量只能被一个线程持有 + +`rt_mutex_trytake`: 等效`rt_mutex_take(mutex, 0);`不等待,如果获取不到直接返回。 + +`rt_mutex_release`: 释放互斥量 + +使用完共享资源的时候,必须尽快释放互斥量,以保证系统的实时性。 + +## 事件集 + +事件集是一个无符号32bit的数,每个事件用一个bit位代表 + +发送:可以从中断或者线程中进行发送 + +接收:线程接收,条件检查(逻辑与方式、逻辑或方式) + +### 事件集API +`rt_event_creat`: 动态创建事件集 + +`rt_event_delete`: 对应`rt_event_creat`。删除事件集对象控制块来释放系统资源,在删除前会唤醒所有挂起在该事件集上的线程(线程的返回值是 -RT_ERROR),然后释放事件集对象占用的内存块 + +`rt_event_init`: 静态创建事件集 + +`rt_event_detach`: 对应`rt_event_init`。将事件集对象从内核对象管理器中脱离 + +`rt_event_send`: 通过参数 set 指定的事件标志来设定 event 事件集对象的事件标志值,然后遍历等待在 event 事件集对象上的等待线程链表,判断是否有线程的事件激活要求与当前 event 对象事件标志值匹配,如果有,则唤醒该线程。 + +`rt_event_recv`: 内核使用 32 位的无符号整数来标识事件集,它的每一位代表一个事件,因此一个事件集对象可同时等待接收 32 个事件,内核可以通过指定选择参数 “逻辑与” 或“逻辑或”来选择如何激活线程,使用 “逻辑与” 参数表示只有当所有等待的事件都发生时才激活线程,而使用 “逻辑或” 参数则表示只要有一个等待的事件发生就激活线程。 + +# 线程间通信 +## 消息邮箱 +特点:是开销比较低,效率较高。 + +邮箱中的每一封邮件只能容纳固定的 4 字节内容(针对 32 位处理系统,指针的大小即为 4 个字节,所以一封邮件恰好能够容纳一个指针)。典型的邮箱也称作交换消息,线程或中断服务例程把一封4字节长度的邮件发送到邮箱中,而一个或多个线程可以从邮箱中接收这些邮件并进行处理。 + +非阻塞方式的邮件发送过程能够安全的应用于中断服务中,是线程、中断服务、定时器向线程发送消息的有效手段。 + +通常来说,邮件收取过程可能是阻塞的,这取决于邮箱中是否有邮件,以及收取邮件时设置的超时时间。当邮箱中不存在邮件且超时时间不为0时,邮件收取过程将变成阻塞方式。在这类情况下,只能由线程进行邮件的收取。 + +当一个线程向邮箱发送邮件时,如果邮箱没满,将把邮件复制到邮箱中。如果邮箱已经满了,发送线程可以设置超时时间,选择等待挂起或直接返回`-RT_EFULL`。如果发送线程选择挂起等待,那么当邮箱中的邮件被收取而空出空间来时,等待挂起的发送线程将被唤醒继续发送。 + +当一个线程从邮箱中接收邮件时,如果邮箱是空的,接收线程可以选择是否等待挂起直到收到新的邮件而唤醒,或可以设置超时时间。当达到设置的超时时间,邮箱依然未收到邮件时,这个选择超时等待的线程将被唤醒并返回`-RT_ETIMEOUT`。如果邮箱中存在邮件,那么接收线程将复制邮箱中的4个字节邮件到接收缓存中。 + +### mailbox API +`rt_mb_create`: 动态创建邮箱对象 + +`rt_mb_delete`: 对应`rt_mb_create`。如果有线程被挂起在该邮箱对象上,内核先唤醒挂起在该邮箱上的所有线程(线程返回值是 -RT_ERROR),然后再释放邮箱使用的内存,最后删除邮箱对象 + +`rt_mb_init`: 静态创建邮箱对象 + +`rt_mb_detach`: 对应`rt_mb_init`。将该邮箱对象从内核对象管理器中脱离 + +`rt_mb_send`: 发送的邮件可以是 32 位任意格式的数据,一个整型值或者一个指向缓冲区的指针。当邮箱中的邮件已经满时,发送邮件的线程或者中断程序会收到 -RT_EFULL 的返回值。 + +`rt_mb_send_wait`: 如果邮箱已经满了,那么发送线程将根据设定的 timeout 参数等待邮箱中因为收取邮件而空出空间。如果设置的超时时间到达依然没有空出空间,这时发送线程将被唤醒并返回错误码。 + +`rt_mb_urgent`: 当发送紧急邮件时,邮件被直接插队放入了邮件队首 + +`rt_mb_recv`: 当接收者接收的邮箱中有邮件时,接收者才能立即取到邮件并返回 RT_EOK 的返回值,否则接收线程会根据超时时间设置,或挂起在邮箱的等待线程队列上,或直接返回 + +## 消息队列 +将多条消息排成的队列形式,是一种常用的线程间通信方式,可以应用在多种场合,线程间的消息交换,使用串口接收不定长数据等。线程可以将一条或多条消息放到消息队列中,同样一个或多个线程可以从消息队列中获得消息同时消息队列提供异步处理机制可以起到缓冲消息的作用。 + +使用消息队列实现线程间的异步通信工作,具有以下特性: +* 支持读消息超时机制 +* 支持等待方式发送消息 +* 允许不同长度(不超过队列节点最大值)任意类型消息 +* 支持发送紧急消息 + +线程或中断服务例程可以将一条或多条消息放入消息队列中。同样,一个或多个线程也可以从消息队列中获得消息。当有多个消息发送到消息队列时,通常将先进入消息队列的消息先传给线程,也就是说,线程先得到的是最先进入消息队列的消息,即先进先出原则(FIFO) + +### 消息队列API +`rt_mq_create`: 动态创建队列,给消息队列对象分配一块内存空间,组织成空闲消息链表,这块内存的大小 =[消息大小 + 消息头(用于链表连接)的大小]X 消息队列最大个数,接着再初始化消息队列,此时消息队列为空 + +`rt_mq_delete`: 对应`rt_mq_create`,如果有线程被挂起在该消息队列等待队列上,则内核先唤醒挂起在该消息等待队列上的所有线程(线程返回值是 - RT_ERROR),然后再释放消息队列使用的内存,最后删除消息队列对象 + +`rt_mq_init`: 静态创建队列 + +`rt_mq_detach`: 对应`rt_mq_init`,将使消息队列对象被从内核对象管理器中脱离 + +`rt_mq_send`: 发送者需指定发送的消息队列的对象句柄(即指向消息队列控制块的指针),并且指定发送的消息内容以及消息大小。在发送一个普通消息之后,空闲消息链表上的队首消息被转移到了消息队列尾 + +`rt_mq_send_wait`: 如果消息队列已经满了,那么发送线程将根据设定的 timeout 参数进行等待。如果设置的超时时间到达依然没有空出空间,这时发送线程将被唤醒并返回错误码 + +`rt_mq_urgent`: 当发送紧急消息时,从空闲消息链表上取下来的消息块不是挂到消息队列的队尾,而是挂到队首 + +`rt_mq_recv`: 当消息队列中有消息时,接收者才能接收消息,否则接收者会根据超时时间设置,或挂起在消息队列的等待线程队列上,或直接返回 + +## 信号 +信号(又称为软中断信号),在软件层次上是对中断机制的一种模拟,在原理上,一个线程收到一个信号与处理器收到一个中断请求可以说是类似的。 + +信号在 RT-Thread 中用作异步通信,POSIX 标准定义了 sigset_t 类型来定义一个信号集,然而 sigset_t 类型在不同的系统可能有不同的定义方式,在 RT-Thread 中,将 sigset_t 定义成了 unsigned long 型,并命名为 rt_sigset_t,应用程序能够使用的信号为 SIGUSR1(10)和 SIGUSR2(12)。 + +信号本质是软中断,用来通知线程发生了异步事件,用做线程之间的异常通知、应急处理。一个线程不必通过任何操作来等待信号的到达,事实上,线程也不知道信号到底什么时候到达,线程之间可以互相通过调用 `rt_thread_kill()` 发送软中断信号。 + +收到信号的线程对各种信号有不同的处理方法,处理方法可以分为三类: + +第一种是类似中断的处理程序,对于需要处理的信号,线程可以指定处理函数,由该函数来处理。 + +第二种方法是,忽略某个信号,对该信号不做任何处理,就像未发生过一样。 + +第三种方法是,对该信号的处理保留系统的默认值。 + +当信号被传递给线程时,如果它正处于挂起状态,那会把状态改为就绪状态去处理对应的信号。如果它正处于运行状态,那么会在它当前的线程栈基础上建立新栈帧空间去处理对应的信号,需要注意的是使用的线程栈大小也会相应增加。 + + +信号的使用一般比较少。它跟中断是差不多的。它主要可以用来事件通知。比如通知线程某个事件已经发生、数据就绪、任务完成之类的。 +也可以用来进行错误的通知。可以实现错误的处理。信号就是软中断,跟中断其实是差不多的。 +### 信号API +`rt_signal_install`:如果线程要处理某一信号,那么就要在线程中安装该信号。安装信号主要用来确定信号值及线程针对该信号值的动作之间的映射关系,即线程将要处理哪个信号,该信号被传递给线程时,将执行何种操作 +在信号安装时设定 handler 参数,决定了该信号的不同的处理方法。处理方法可以分为三种: + +1)类似中断的处理方式,参数指向当信号发生时用户自定义的处理函数,由该函数来处理。 + +2)参数设为 SIG_IGN,忽略某个信号,对该信号不做任何处理,就像未发生过一样。 + +3)参数设为 SIG_DFL,系统会调用默认的处理函数_signal_default_handler()。 + +`rt_signal_mask`: 如果该信号被阻塞,则该信号将不会递达给安装此信号的线程,也不会引发软中断处理 + +`rt_signal_unmask`: 线程中可以安装好几个信号,使用此函数可以对其中一些信号给予 “关注”,那么发送这些信号都会引发该线程的软中断 + +`rt_signal_kill`: 给设定了处理异常的线程发送信号 + +`rt_signal_wait`: 等待 set 信号的到来,如果没有等到这个信号,则将线程挂起,直到等到这个信号或者等待时间超过指定的超时时间 timeout。如果等到了该信号,则将指向该信号体的指针存入 si \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day4-Device.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day4-Device.md" new file mode 100644 index 0000000000000000000000000000000000000000..5b76702536540dba7b27d029ac91d314278c1c3b --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day4-Device.md" @@ -0,0 +1,144 @@ +# 驱动 +本质上是为了方便进行代码移植,在硬件直接驱动层上面又套了一层,只要使用RTT系统,那么本层以上的代码就已经抽象出来了,在硬件更换或者其他操作的时候调用的API是独立的。 + +而本层下面的硬件直接交互层,可以被抽象成寄存器,甚至HAL/STD库。做不同板子移植的时候仅需要关心本层的内容,只要移植正确,上层应用肯定是可以正常使用的。 + +整体思路: + +``` +设备驱动 + | +统一API + | +底层对接驱动 +``` +## RTT设备驱动框架 +``` +I/O层: rt_object + | | +设备驱动框架层: + | | | + | rt_serial_device , rt_adc_device ... +设备驱动层: + | | | + | stm32_uart xxx_uart ..... + | bsp/stm32/drv_uart.c +SDK层:HAL或者直接操作寄存器 +硬件 +``` +`本层的操作有点像Linux中的posix层` + +I/O提供了统一的open write read control close接口 + + +## RTT device API +`rt_device_create`创建设备 + +`rt_device_destroy `销毁设备 + +`rt_device_register`注册设备 + +`rt_device_unregister`注销设备 + +`rt_device_find`查找设备 + +`rt_device_init`初始化设备 + +`rt_device_control`控制设备 + +`rt_device_read`读设备 + +`rt_device_write`写设备 + +`rt_device_set_rx_indicate`接收回调函数 + +`rt_device_set_tx_complete`发送回调函数 + +## 访问IO设备 +``` +APP---I/O设备管理接口---I/O设备操作方法---硬件 + | | + rt_device_open()---open() +``` +IO框架调用关系图 +``` +rt_device_init + | + ops->init + | + end + +rt_device_open + | +ops->init(若未初始化) + | + ops->open + | +dev->ref_count++ + | + end + +rt_device_write + | + ops->open + | + end +``` +### GPIO开发 +可编程控制中断:一般有五种中断触发模式: + +* 上升沿触发 +* 下降沿触发 +* 高电平触发 +* 低电平触发 +* 双边沿触发 + +app通过PIN设备管理接口访问GPIO: +`rt_pin_mode`:设置引脚模式 + +`rt_pin_write`:设置引脚电平 + +`rt_pin_read`: 读取电平 + +`rt_pin_attach_irq`: 绑定引脚中断回调函数 + +`rt_pin_irq_enable`: 使能引脚中断 + +`rt_pin_detach_irq`: 脱离引脚中断回调函数 + +使用外部中断: + +设置pin模式,注册回调函数,使能回调函数; +打开ulog,在env里通过`/`搜索component--utiliti--ulog--使能 isr log + +### I2C +硬件iic sdk--io--上层 + +软件iic pin框架--软件层(多抽象了一层)--io--上层 + +I2C总线死锁: + +当I2C主机正在和从机通信时,如果主机正好发生打算发第9个时钟,此时SCL为高,而从机开始拉低SDA为低做准备(作为ACK信号),等待主机SCL变低后,从再释放SDA为高。 + +如果此时正好主机复位,主机SCL还没来得及变低,直接变成高电平,此时从机还在等待SCL变低,所以一直拉低SDA;而主机由于复位,发现SDA一直为低,也在等待从放SDA为高。因此主机和从机都进入一个相互等待的死锁状态。 + +I2C设备探测package:`i2c-tools` +### SPI +`rt_spi_bus_attach_device`挂载SPI设备到SPI总线,user_data一般是片选引脚 + +`rt_hw_spi_device_attach`使用stm32的bsp也可以使用这个函数挂载spi设备到总线 + +`rt_spi_configure`配置spi + +`rt_spi_transfer`传输一次数据 + +`rt_spi_send`发送一次数据,本质还是调用transfer + +`rt_spi_recv`接受一次数据,本质是调用transfer + +`rt_spi_send_then_send`连续发送两次 + +`rt_spi_send_then_recv`先发送再接收 + +`rt_spi_transfer_message`自定义传输数据 + diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day5-Package.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day5-Package.md" new file mode 100644 index 0000000000000000000000000000000000000000..d870addcb56d89cade9d8188adff555a2d595fa3 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day5-Package.md" @@ -0,0 +1,61 @@ +# 软件包 +ENV实际操控的是`.config`文件,在选择了新的软件包后对应的文件会做出变化. `.config`会影响`rtconfig.h` 实际上工程编译的时候使用`rtconfig.h` + +如果env没拉取到最新软件包,可以在env文件夹 `env-windows\packages\packages`里用`git pull`命令手动更新 + +使用`/`可以快速搜索 + +## LLM-chat pkg的使用 +api从https://bailian.console.aliyun.com/?tab=model#/model-market注册 + +* 修改Maxium fragment length in bytes 为6144 + +* 使能MbedTLS + +* 使能网络驱动EMAC driver + +llm-chat依赖于其他三个软件包,会一起下载下来 + +# kconfig和scons +kconfig->.config->rtconfig.h + +kconfig会生成.config文件,scons系统通过python将其变为rtconfig.h,令编译器能够编译 + +kconfig的匹配关系:source上级目录;source当前目录的子目录 + +使用source进行逐级的kconfig手机, 这样可以做到模块间配置分离. + +## kconfig主要语法 +kconfig的语法中从属关系和python类似,使用缩进作为段落 + +`config`创建一个选项,启用会创建对应的宏,可以是`bool` `tristate` `string` `int` `hex`类型的 + +`menuconfig`创建一个菜单,需要用endmenu结尾 + +`choice`二选一或者多选一,互斥的选项 + +`select`强制依赖,select的目标会被强制选中,不能手动关闭 + +`help`选项的注释,可以在选项上用`?`查看 + +`depends on`依赖,依赖的宏被定义后这个选项才会被显示 + +`comment`定义一些提示信息 + +`if`条件判断,使用`endif`结尾 + +`source`用于读取另一个文件中的 Kconfig 文件 + +## scons +scons由sconsript(每个文件夹都可以用,通过遍历使用)和sconstruct(唯一,指定编译参数、工具链等内容)组成,由python编写,用法在Day2笔记中有记载 + +# 联网碰到的问题 +使用qemuA9工程联网的几个要点: +1. 安装虚拟网卡. +2. 共享网络. +3. 在`menuconfig`中打开`Onboard Driver`中的 `EMAC driver` +4. 修改对应的`qemu.bat`和`qemu-nographic.bat` +5. 对工程重新进行`scons` +6. 使用`ifconfig`查看是否有网络IP + +如果出现`network interface device list error` 请检查`Step 3`