04-单片机商业项目编程,从零搭建低功耗系统设计

一、本文内容

    上一节《03-单片机商业项目编程,从零搭建低功耗系统设计-CSDN博客》我们确定了设计思路,并如何更有效的保持低功耗,这节我们就准备来做软件框架设计。在AI飞速发展的时代,我们也会利AI来辅助我们完成,让自己也能慢慢学会和AI的合作。

二、软件设计思路

    对于软件代码编写,最重要的还是框架搭建,并不是要一开始就把所有代码一五一十的写出来,而是框架搭建好了,再去填充自己需要的内容,比如单片机的底层驱动之类的;我们软件设计第一步先确定方案构思,第二步开始搭建软件框架,第三步移植到指定芯片。

    当前AI发展十分迅速,很多算法类的代码,都可以不需要自己去编写,而我们更多的是发现问题,修正问题,这样既能够让AI给我们提供思路,又能充分发现AI的思路错误,来理顺自己的思路。我们今天利用Chat GPT来帮我们以STM32F030C8T6构建基础软件框架设计,我们不要求他能够直接能运行,但是要保障他的代码逻辑没有漏洞。这也是我们作为程序员和AI协同的提前练习;当然你也可以选择国内其它AI工具来实现。

三、软件编写过程

    我:能帮我基于STM32F030C8T6,利用systick做一个基于事件触发的低功耗伪时间片调度算法吗?

    GPT:当然可以。以下是一个基于STM32F030C8T6的低功耗伪时间片调度算法的示例。这个例子使用了systick定时器来触发事件。

#include "stm32f0xx.h"// 定义任务结构体
typedef struct {void (*taskFunc)(void); // 任务函数指针uint32_t delay; // 任务延迟uint32_t period; // 任务周期uint8_t run; // 运行标志
} sTask;#define SCH_MAX_TASKS 5 // 最大任务数sTask SCH_tasks_G[SCH_MAX_TASKS]; // 任务数组void SysTick_Handler(void) {uint32_t index;for (index = 0; index < SCH_MAX_TASKS; index++) {if ((SCH_tasks_G[index].taskFunc != 0) && (SCH_tasks_G[index].delay == 0)) {SCH_tasks_G[index].run = 1; // 标记任务为可运行if (SCH_tasks_G[index].period) {SCH_tasks_G[index].delay = SCH_tasks_G[index].period; // 重置任务延迟}}if (SCH_tasks_G[index].delay > 0) {SCH_tasks_G[index].delay--; // 任务延迟递减}}
}void SCH_Dispatch_Tasks(void) {uint32_t index;for (index = 0; index < SCH_MAX_TASKS; index++) {if (SCH_tasks_G[index].run == 1) {(*SCH_tasks_G[index].taskFunc)(); // 运行任务SCH_tasks_G[index].run = 0; // 清除运行标志}}
}void SCH_Add_Task(void (*fPtr)(void), const uint32_t DELAY, const uint32_t PERIOD) {uint32_t Index = 0;while ((SCH_tasks_G[Index].taskFunc != 0) && (Index < SCH_MAX_TASKS)) {Index++;}if (Index < SCH_MAX_TASKS) {SCH_tasks_G[Index].taskFunc = fPtr;SCH_tasks_G[Index].delay = DELAY;SCH_tasks_G[Index].period = PERIOD;SCH_tasks_G[Index].run = 0;}
}int main(void) {// 初始化systick定时器SysTick_Config(SystemCoreClock / 1000);// 添加任务SCH_Add_Task(Task1, 0, 1000);SCH_Add_Task(Task2, 0, 2000);// 主循环while (1) {SCH_Dispatch_Tasks();__WFI(); // 进入低功耗模式}
}

    这个例子中,我们定义了一个任务结构体,包含了任务函数指针、任务延迟、任务周期和运行标志。在systick定时器中断服务程序中,我们检查每个任务,如果任务延迟为0,我们就标记任务为可运行,并重置任务延迟。在主循环中,我们检查每个任务,如果任务被标记为可运行,我们就运行任务,并清除运行标志。

    这个例子只是一个基本的框架,你可能需要根据你的具体需求进行修改。希望这个例子能对你有所帮助!

    我:低功耗对我非常重要,你写的代码需要每1ms就要进行一次中断判断,实际还是做了很多无效判断,我希望是动态的调整systick的定时任务,计算出下一任务还需要多长时间执行,就设定多长时间的中断,这样保障系统尽可能的在休眠状态,提高执行效率

    GPT:我理解你的需求,你希望系统能够动态地调整systick的定时任务,以便在尽可能多的时间里让系统处于休眠状态,从而提高执行效率。以下是一个修改后的示例,它会动态地调整systick的定时任务:

#include "stm32f0xx.h"typedef struct {void (*taskFunc)(void);uint32_t delay;uint32_t period;uint8_t run;
} sTask;#define SCH_MAX_TASKS 5sTask SCH_tasks_G[SCH_MAX_TASKS];void SysTick_Handler(void) {uint32_t index;for (index = 0; index < SCH_MAX_TASKS; index++) {if ((SCH_tasks_G[index].taskFunc != 0) && (SCH_tasks_G[index].delay == 0)) {SCH_tasks_G[index].run = 1;if (SCH_tasks_G[index].period) {SCH_tasks_G[index].delay = SCH_tasks_G[index].period;}}if (SCH_tasks_G[index].delay > 0) {SCH_tasks_G[index].delay--;}}// 重新计算下一次中断的时间uint32_t nextInterrupt = UINT32_MAX;for (index = 0; index < SCH_MAX_TASKS; index++) {if (SCH_tasks_G[index].taskFunc != 0 && SCH_tasks_G[index].delay < nextInterrupt) {nextInterrupt = SCH_tasks_G[index].delay;}}// 设置下一次中断的时间SysTick->LOAD = nextInterrupt;SysTick->VAL = 0;
}void SCH_Dispatch_Tasks(void) {uint32_t index;for (index = 0; index < SCH_MAX_TASKS; index++) {if (SCH_tasks_G[index].run == 1) {(*SCH_tasks_G[index].taskFunc)();SCH_tasks_G[index].run = 0;}}
}void SCH_Add_Task(void (*fPtr)(void), const uint32_t DELAY, const uint32_t PERIOD) {uint32_t Index = 0;while ((SCH_tasks_G[Index].taskFunc != 0) && (Index < SCH_MAX_TASKS)) {Index++;}if (Index < SCH_MAX_TASKS) {SCH_tasks_G[Index].taskFunc = fPtr;SCH_tasks_G[Index].delay = DELAY;SCH_tasks_G[Index].period = PERIOD;SCH_tasks_G[Index].run = 0;}
}int main(void) {SysTick_Config(SystemCoreClock / 1000);SCH_Add_Task(Task1, 0, 1000);SCH_Add_Task(Task2, 0, 2000);while (1) {SCH_Dispatch_Tasks();__WFI();}
}

    在这个修改后的示例中,我在SysTick_Handler函数中添加了一段代码,用于计算下一次中断的时间,并设置systick定时器的加载值。这样,systick定时器的中断时间就会根据任务的需求动态地进行调整,从而使系统能够在尽可能多的时间里处于休眠状态,提高执行效率。希望这个修改后的示例能满足你的需求!

    我:SysTick_Handler()函数能够把每句话加上注释吗,有点看不懂

    GPT:当然可以,我会为SysTick_Handler()函数中的每一行代码添加注释,以帮助你理解。以下是带有注释的代码:

void SysTick_Handler(void) {uint32_t index;// 遍历任务数组for (index = 0; index < SCH_MAX_TASKS; index++) {// 如果任务函数指针不为空且任务延迟为0if ((SCH_tasks_G[index].taskFunc != 0) && (SCH_tasks_G[index].delay == 0)) {// 标记任务为可运行SCH_tasks_G[index].run = 1;// 如果任务周期不为0,则重置任务延迟if (SCH_tasks_G[index].period) {SCH_tasks_G[index].delay = SCH_tasks_G[index].period;}}// 如果任务延迟大于0,则递减任务延迟if (SCH_tasks_G[index].delay > 0) {SCH_tasks_G[index].delay--;}}// 初始化下一次中断的时间为最大值uint32_t nextInterrupt = UINT32_MAX;// 再次遍历任务数组for (index = 0; index < SCH_MAX_TASKS; index++) {// 如果任务函数指针不为空且任务延迟小于下一次中断的时间if (SCH_tasks_G[index].taskFunc != 0 && SCH_tasks_G[index].delay < nextInterrupt) {// 更新下一次中断的时间nextInterrupt = SCH_tasks_G[index].delay;}}// 设置systick定时器的加载值为下一次中断的时间SysTick->LOAD = nextInterrupt;// 清零systick定时器的当前值SysTick->VAL = 0;
}

    这段代码的主要目的是在每次systick定时器中断时更新任务的状态,并计算下一次中断的时间。希望这些注释能帮助你理解这段代码!

    我:我发现这段代码由逻辑错误,因为我们的下一次中断是按最短延时来计算,也就是可能是不定值的毫秒数,而代码这里却每次只是减1操作:// 如果任务延迟大于0,则递减任务延迟

if (SCH_tasks_G[index].delay > 0) {
SCH_tasks_G[index].delay--;
}

    GPT:你的观察非常准确,这确实是一个问题。在这个代码中,我们假设每次中断的时间间隔是1ms,所以每次中断我们只减少1ms的延迟。但是,如果我们动态地改变中断的时间间隔,那么我们就需要减少相应的延迟时间,而不是只减少1ms。我们可以通过修改SysTick_Handler函数来解决这个问题。具体来说,我们可以在计算出下一次中断的时间后,将这个时间间隔从所有任务的延迟中减去。以下是修改后的代码:

void SysTick_Handler(void) {uint32_t index;// 初始化下一次中断的时间为最大值uint32_t nextInterrupt = UINT32_MAX;// 遍历任务数组for (index = 0; index < SCH_MAX_TASKS; index++) {// 如果任务函数指针不为空且任务延迟小于下一次中断的时间if (SCH_tasks_G[index].taskFunc != 0 && SCH_tasks_G[index].delay < nextInterrupt) {// 更新下一次中断的时间nextInterrupt = SCH_tasks_G[index].delay;}}// 遍历任务数组for (index = 0; index < SCH_MAX_TASKS; index++) {// 如果任务函数指针不为空if (SCH_tasks_G[index].taskFunc != 0) {// 如果任务延迟大于下一次中断的时间if (SCH_tasks_G[index].delay >= nextInterrupt) {// 减去下一次中断的时间SCH_tasks_G[index].delay -= nextInterrupt;// 如果任务延迟为0if (SCH_tasks_G[index].delay == 0) {// 标记任务为可运行SCH_tasks_G[index].run = 1;// 如果任务周期不为0,则重置任务延迟if (SCH_tasks_G[index].period) {SCH_tasks_G[index].delay = SCH_tasks_G[index].period;}}}}}// 设置systick定时器的加载值为下一次中断的时间SysTick->LOAD = nextInterrupt;// 清零systick定时器的当前值SysTick->VAL = 0;
}

    希望这个修改后的代码能满足你的需求!

    我:感觉还是有点问题,nextInterrupt在前面就获得值了,然后再去遍历任务数组,那不等于本次调用SysTick_Handler中断处理函数,就立即对应的任务标记可运行了?应该是中断下一轮,才到吧,实际上nextInterrupt要定义成static变量,然后更新其值要放后面吧

    GPT:你的观察非常准确,我理解你的担忧。确实,我们应该在下一轮中断时才标记任务为可运行。为了解决这个问题,我们可以将nextInterrupt定义为静态变量,并在每次中断时使用它来更新任务的延迟,然后在中断的最后更新nextInterrupt的值。以下是修改后的代码:

void SysTick_Handler(void) {static uint32_t nextInterrupt = 1; // 初始化为1,以便在第一次中断时运行uint32_t index;// 遍历任务数组for (index = 0; index < SCH_MAX_TASKS; index++) {// 如果任务函数指针不为空if (SCH_tasks_G[index].taskFunc != 0) {// 如果任务延迟大于等于下一次中断的时间if (SCH_tasks_G[index].delay >= nextInterrupt) {// 减去下一次中断的时间SCH_tasks_G[index].delay -= nextInterrupt;// 如果任务延迟为0if (SCH_tasks_G[index].delay == 0) {// 标记任务为可运行SCH_tasks_G[index].run = 1;// 如果任务周期不为0,则重置任务延迟if (SCH_tasks_G[index].period) {SCH_tasks_G[index].delay = SCH_tasks_G[index].period;}}}}}// 初始化下一次中断的时间为最大值nextInterrupt = UINT32_MAX;// 再次遍历任务数组for (index = 0; index < SCH_MAX_TASKS; index++) {// 如果任务函数指针不为空且任务延迟小于下一次中断的时间if (SCH_tasks_G[index].taskFunc != 0 && SCH_tasks_G[index].delay < nextInterrupt) {// 更新下一次中断的时间nextInterrupt = SCH_tasks_G[index].delay;}}// 设置systick定时器的加载值为下一次中断的时间SysTick->LOAD = nextInterrupt;// 清零systick定时器的当前值SysTick->VAL = 0;
}

    这样,我们就可以确保只有在下一轮中断时才会标记任务为可运行。希望这个修改后的代码能满足你的需求!

    我:嗯,太好了,这个中断处理函数目前没问题了,那么对应的SCH_Add_Task函数也要做一些修正,新增了任务后,对应的nextInterrupt参数和SysTick也要更新

    GPT:你的观察非常准确,当我们添加新的任务时,我们确实需要更新nextInterrupt和systick定时器。以下是修改后的SCH_Add_Task函数:

void SCH_Add_Task(void (*fPtr)(void), const uint32_t DELAY, const uint32_t PERIOD) {uint32_t Index = 0;// 寻找任务数组中的空闲位置while ((SCH_tasks_G[Index].taskFunc != 0) && (Index < SCH_MAX_TASKS)) {Index++;}// 如果找到空闲位置if (Index < SCH_MAX_TASKS) {// 添加新任务SCH_tasks_G[Index].taskFunc = fPtr;SCH_tasks_G[Index].delay = DELAY;SCH_tasks_G[Index].period = PERIOD;SCH_tasks_G[Index].run = 0;// 如果新任务的延迟小于下一次中断的时间if (DELAY < nextInterrupt) {// 更新下一次中断的时间nextInterrupt = DELAY;// 设置systick定时器的加载值为下一次中断的时间SysTick->LOAD = nextInterrupt;// 清零systick定时器的当前值SysTick->VAL = 0;}}
}

    在这个修改后的函数中,当我们添加新的任务时,我们会检查新任务的延迟是否小于下一次中断的时间。如果是,我们就更新下一次中断的时间,并设置systick定时器的加载值为下一次中断的时间。希望这个修改后的函数能满足你的需求!

    我:那再把上面确定的这些,全部写出来,我再梳理下

    GPT:当然,以下是我们讨论并确定的代码:

#include "stm32f0xx.h"typedef struct {void (*taskFunc)(void);uint32_t delay;uint32_t period;uint8_t run;
} sTask;#define SCH_MAX_TASKS 5sTask SCH_tasks_G[SCH_MAX_TASKS];
static uint32_t nextInterrupt = 1; // 初始化为1,以便在第一次中断时运行void SysTick_Handler(void) {uint32_t index;// 遍历任务数组for (index = 0; index < SCH_MAX_TASKS; index++) {// 如果任务函数指针不为空if (SCH_tasks_G[index].taskFunc != 0) {// 如果任务延迟大于等于下一次中断的时间if (SCH_tasks_G[index].delay >= nextInterrupt) {// 减去下一次中断的时间SCH_tasks_G[index].delay -= nextInterrupt;// 如果任务延迟为0if (SCH_tasks_G[index].delay == 0) {// 标记任务为可运行SCH_tasks_G[index].run = 1;// 如果任务周期不为0,则重置任务延迟if (SCH_tasks_G[index].period) {SCH_tasks_G[index].delay = SCH_tasks_G[index].period;}}}}}// 初始化下一次中断的时间为最大值nextInterrupt = UINT32_MAX;// 再次遍历任务数组for (index = 0; index < SCH_MAX_TASKS; index++) {// 如果任务函数指针不为空且任务延迟小于下一次中断的时间if (SCH_tasks_G[index].taskFunc != 0 && SCH_tasks_G[index].delay < nextInterrupt) {// 更新下一次中断的时间nextInterrupt = SCH_tasks_G[index].delay;}}// 设置systick定时器的加载值为下一次中断的时间SysTick->LOAD = nextInterrupt;// 清零systick定时器的当前值SysTick->VAL = 0;
}void SCH_Add_Task(void (*fPtr)(void), const uint32_t DELAY, const uint32_t PERIOD) {uint32_t Index = 0;// 寻找任务数组中的空闲位置while ((SCH_tasks_G[Index].taskFunc != 0) && (Index < SCH_MAX_TASKS)) {Index++;}// 如果找到空闲位置if (Index < SCH_MAX_TASKS) {// 添加新任务SCH_tasks_G[Index].taskFunc = fPtr;SCH_tasks_G[Index].delay = DELAY;SCH_tasks_G[Index].period = PERIOD;SCH_tasks_G[Index].run = 0;// 如果新任务的延迟小于下一次中断的时间if (DELAY < nextInterrupt) {// 更新下一次中断的时间nextInterrupt = DELAY;// 设置systick定时器的加载值为下一次中断的时间SysTick->LOAD = nextInterrupt;// 清零systick定时器的当前值SysTick->VAL = 0;}}
}void SCH_Dispatch_Tasks(void) {uint32_t index;for (index = 0; index < SCH_MAX_TASKS; index++) {if (SCH_tasks_G[index].run == 1) {(*SCH_tasks_G[index].taskFunc)();SCH_tasks_G[index].run = 0;}}
}int main(void) {SysTick_Config(SystemCoreClock / 1000);SCH_Add_Task(Task1, 0, 1000);SCH_Add_Task(Task2, 0, 2000);while (1) {SCH_Dispatch_Tasks();__WFI();}
}

    这段代码实现了一个基于STM32F030C8T6的低功耗伪时间片调度算法,使用了systick定时器来触发事件,并且能够动态地调整systick的定时任务,以...(GPT卡壳了,但不重要)

    我:我觉得在SysTick_Handler()函数中if (SCH_tasks_G[index].delay >= nextInterrupt) 的else情况也写一下,这样更健全点儿,当不知名原因导致delay小于nextInterrupt,定时时间肯定也是到了

    GPT:你的建议非常好,确实应该考虑到这种情况。我们可以在if (SCH_tasks_G[index].delay >= nextInterrupt)后面添加一个else语句,处理delay小于nextInterrupt的情况。以下是修改后的代码:

#include "stm32f0xx.h"typedef struct {void (*taskFunc)(void);uint32_t delay;uint32_t period;uint8_t run;
} sTask;#define SCH_MAX_TASKS 5sTask SCH_tasks_G[SCH_MAX_TASKS];
static uint32_t nextInterrupt = 1; // 初始化为1,以便在第一次中断时运行void SysTick_Handler(void) {uint32_t index;// 遍历任务数组for (index = 0; index < SCH_MAX_TASKS; index++) {// 如果任务函数指针不为空if (SCH_tasks_G[index].taskFunc != 0) {// 如果任务延迟大于等于下一次中断的时间if (SCH_tasks_G[index].delay >= nextInterrupt) {// 减去下一次中断的时间SCH_tasks_G[index].delay -= nextInterrupt;// 如果任务延迟为0if (SCH_tasks_G[index].delay == 0) {// 标记任务为可运行SCH_tasks_G[index].run = 1;// 如果任务周期不为0,则重置任务延迟if (SCH_tasks_G[index].period) {SCH_tasks_G[index].delay = SCH_tasks_G[index].period;}}} else {// 如果任务延迟小于下一次中断的时间// 标记任务为可运行SCH_tasks_G[index].run = 1;SCH_tasks_G[index].delay = 0;// 如果任务周期不为0,则重置任务延迟if (SCH_tasks_G[index].period) {SCH_tasks_G[index].delay = SCH_tasks_G[index].period;}}}}// 初始化下一次中断的时间为最大值nextInterrupt = UINT32_MAX;// 再次遍历任务数组for (index = 0; index < SCH_MAX_TASKS; index++) {// 如果任务函数指针不为空且任务延迟小于下一次中断的时间if (SCH_tasks_G[index].taskFunc != 0 && SCH_tasks_G[index].delay < nextInterrupt) {// 更新下一次中断的时间nextInterrupt = SCH_tasks_G[index].delay;}}// 设置systick定时器的加载值为下一次中断的时间SysTick->LOAD = nextInterrupt;// 清零systick定时器的当前值SysTick->VAL = 0;
}void SCH_Add_Task(void (*fPtr)(void), const uint32_t DELAY, const uint32_t PERIOD) {uint32_t Index = 0;// 寻找任务数组中的空闲位置while ((SCH_tasks_G[Index].taskFunc != 0) && (Index < SCH_MAX_TASKS)) {Index++;}// 如果找到空闲位置if (Index < SCH_MAX_TASKS) {// 添加新任务SCH_tasks_G[Index].taskFunc = fPtr;SCH_tasks_G[Index].delay = DELAY;SCH_tasks_G[Index].period = PERIOD;SCH_tasks_G[Index].run = 0;// 如果新任务的延迟小于下一次中断的时间if (DELAY < nextInterrupt) {// 更新下一次中断的时间nextInterrupt = DELAY;// 设置systick定时器的加载值为下一次中断的时间SysTick->LOAD = nextInterrupt;// 清零systick定时器的当前值SysTick->VAL = 0;}}
}void SCH_Dispatch_Tasks(void) {uint32_t index;for (index = 0; index < SCH_MAX_TASKS; index++) {if (SCH_tasks_G[index].run == 1) {(*SCH_tasks_G[index].taskFunc)();SCH_tasks_G[index].run = 0;}}
}void Task1(void)
{}void Task2(void)
{}int main(void) {SysTick_Config(SystemCoreClock / 1000);SCH_Add_Task(Task1, 0, 1000);SCH_Add_Task(Task2, 0, 2000);while (1) {SCH_Dispatch_Tasks();__WFI();}
}

    在这个修改后的代码中,如果任务延迟小于下一次中断的时间,我们会立即标记任务为可运行,并重置任务延迟。这样,我们就可以确保在任何情况下,只...(GPT又卡壳了,不重要)

四、总结

    是不是和我们平时单片机编程很不一样,实际上AI编程它总结了很多前任的编程经验,以及足够的框架规范,所以它的整体框架还是不错的,但是它并不会很好的完成每一个细节,甚至有一些bug,这反而正是我们自己已经梳理好的,所以我们给它指导出来即可;这样就和AI协同完成了一个基本框架,接下来自己要做的就是移植到指定的芯片上,完成基本的底层驱动开发即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/327175.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络工程师----第二十八天

计算机基础 第五章&#xff1a;运输层 运输层的两个协议&#xff1a; 1、传输控制协议TCP&#xff1a; TCP最主要的特点&#xff1a; (1)TCP是面向连接的。应用程序在使用TCP协议之前&#xff0c;必须先建立连接。在传送数据完毕后&#xff0c;必须释放已经建立的TCP连接。…

大语言模型的RAG:综述

23年12月同济大学和复旦大学的综述论文“Retrieval-Augmented Generation for Large Language Models: A Survey”。 大语言模型&#xff08;LLM&#xff09;展示了强大的功能&#xff0c;但在实际应用中仍然面临挑战&#xff0c;如幻觉、知识更新缓慢以及答案缺乏透明度。检索…

怎么转换音频?看这3款音频转换器

随着数字媒体的发展&#xff0c;音频文件在我们的日常生活中占据了越来越重要的地位。有时候在不同的应用场景里&#xff0c;无论是音乐、语音还是其他类型的音频内容&#xff0c;我们都需要对其进行转换以满足不同的需求。 本文将为您介绍3款常用的音频转换器&#xff0c;帮助…

MHD093C-058-PG1-AA具备哪些特点?

MHD093C-058-PG1-AA是一种高性能的伺服电机控制器。 该产品具备以下特点&#xff1a; 高精度与高性能&#xff1a;MHD093C-058-PG1-AA设计用于提供精确的运动控制和定位&#xff0c;适用于需要高精度定位和控制的场合。快速响应&#xff1a;采用先进的控制技术&#xff0c;确…

Memcached

一、NoSQL介绍 NoSQL是对 Not Only SQL、非传统关系型数据库的统称。 NoSQL一词诞生于1998年&#xff0c;2009年这个词汇被再次提出指非关系型、分布式、不提供ACID的数据库设计模式。 随着互联网时代的到来&#xff0c;数据爆发式增长&#xff0c;数据库技术发展日新月异&a…

基础算法,贪心算法,贪心策略,OJ练习

文章目录 一、概念二、OJ练习2.1 区间选点2.2 区间合并2.3 区间2.4 合并果子2.5 排队接水2.6 货仓选址2.7 防晒2.8 畜栏预定2.9 雷达设备2.10 国王游戏2.11 耍杂技的牛2.12 给树染色2.13 任务2.14 能量石 三、总结 一、概念 贪心是一种在每次决策时采取当前意义下最优策略的算…

[JAVASE] 类和对象(二)

目录 一. 封装 1.1 面向对象的三大法宝 1.2 封装的基本定义与实现 二. 包 2.1 包的定义 2.2 包的作用 2.3 包的使用 2.3.1 导入类 2.3.2 导入静态方法 三. static 关键字 (重要) 3.1 static 的使用 (代码例子) 3.1.1 3.1.2 3.1.3 3.1.4 四. 总结 一. 封装 1.1 面向对象…

windows 安装 Conda

1 Conda简介 Conda 是一个开源的软件包管理系统和环境管理系统,用于安装多个版本的软件包及其依赖关系,并在它们之间轻松切换。Conda 是为 Python 程序创建的,适用于 Linux,OS X 和Windows,也可以打包和分发其他软件。一般用conda来维护多个python版本。 2 安装…

IDEA报错:java 找不到符号

IDEA报错:java 找不到符号,代码没问题,IDEA缓存也清理了也重新构建了就是不行 最后使用终极大法 -Djps.track.ap.dependenciesfalse

NMACDR:基于邻居交互增强和多头注意力机制的跨域推荐模型

基于邻居交互增强和多头注意力机制的跨域推荐模型 湖北民族大学学报-孙克雷、汪盈盈-2023 思路 针对基于映射的跨域推荐模型没有充分关注源域中数据稀疏的用户,导致用户偏好的迁移效率降低的问题,提出本文。 首先,利用邻居用户的交互来增强源域中数据稀疏用户的交互序列,…

UNITY报错:An error occurred while resolving packages: Project has invalid dependencies: com.unit

打开unity出现了这样的报错&#xff1a; An error occurred while resolving packages: Project has invalid dependencies: com.unity.render-pipelines.universal: Package [com.unity.render-pipelines.universal12.1.2] cannot be found 这里在同站其他博主提供的方…

Python爬虫逆向——某公开数据网站实例小记(二)

注意&#xff01;&#xff01;&#xff01;&#xff01;某XX网站逆向实例仅作为学习案例&#xff0c;禁止其他个人以及团体做谋利用途&#xff01;&#xff01;&#xff01; 第一步&#xff1a;分析页面和请求方式 aHR0cHM6Ly95Z3AuZ2R6d2Z3Lmdvdi5jbi8jLzQ0L2p5Z2c 此网站经…

Oracle21c数据库普通用户创建及授权,建表,创建存储过程、序列、触发器

一、Oracle数据库错误 ORA-65096 表示你尝试在多租户容器数据库&#xff08;CDB&#xff09;环境中创建一个公共用户&#xff08;common user&#xff09;或角色&#xff0c;但没有使用正确的前缀。在多租户架构中&#xff0c;公共用户的用户名必须以 C## 或 c## 开头。 若想…

【python】将json内解码失败的中文修改为英文(‘utf-8‘ codec can‘t decode,labelme标注时文件名未中文)

出现问题的场景&#xff1a; 语义分割数据集&#xff0c;使用labelme工具进行标注&#xff0c;然后标注图片存在中文名&#xff0c;导致json标签文件写入中文图片名&#xff0c;从而解析失败。 代码解析json文件时&#xff0c;出现报错&#xff1a; python脚本需求&#x…

uniapp日期区间选择器

uniapp日期区间选择器 在 uniapp 中创建一个简单的自定义日期范围的日期区间选择器&#xff1a; - 限制有效日期范围开始日期为 2024-01-01&#xff0c;结束日期为当日&#xff1b; - 默认日期区间为当日向前计算的7日区间&#xff1b; - 选择开始时间后&#xff0c;判断不可大…

【Gaea+UE5】创建基本的大型世界场景

目录 效果 步骤 一、在Gaea中生成地形 二、确定导出的地形规模 三、在UE中创建地形 四、验证UE创建的地形规模是否正确 五、使用M4自动地形材质 效果 步骤 一、在Gaea中生成地形 1. 打开Gaea官网下载软件 2. 打开Gaea软件&#xff0c;我们可以选择一个预设的山体 创…

C++哈希(个人笔记)

C哈希 1.unordered_mapd1.1unordered_map的构造函数1.2unorder_map的容量1.3unordered_map的迭代器1.4unordered_map的元素访问1.5unorder_map的查找1.6unordered_map的修改操作1.7unordered_map的桶操作 2.unordered_set3.unordered_set和unordered_set的笔试题4.哈希4.1哈希概…

Java全局异常处理,@ControllerAdvice异常拦截原理解析【简单易懂】

https://www.bilibili.com/video/BV1sS411c7Mo 文章目录 一、全局异常处理器的类型1-1、实现方式一1-2、实现方式二 二、全局异常拦截点2-1、入口2-2、全局异常拦截器是如何注入到 DispatcherServlet 的 三、ControllerAdvice 如何解析、执行3-1、解析3-2、执行 四、其它4-1、设…

CorelDRAW2024新特性全解析!

CorelDRAW2024是一款备受赞誉的图形设计软件&#xff0c;它以其强大的功能和用户友好性赢得了全球数百万设计师的青睐。该软件提供了丰富的绘图、排版、图像处理、矢量编辑以及网页设计工具&#xff0c;无论是初学者还是专业设计师&#xff0c;都能在这款软件中找到满足自己需求…

精品录播|电磁场数值仿真技术及天线设计与应用

电磁场数值仿真技术及天线设计与应用