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.mzph.cn/web/12261.shtml

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

相关文章

浅谈C++ overload(重载) override(覆盖) overwrite(重写)

目录 1. 名词辨析2 含义解析1 overload重载2 override覆盖3 overwrite重写 3 区别4 代码示例 1. 名词辨析 关于这3个名词的中文翻译&#xff1a; overload翻译为重载&#xff0c;基本是没有歧义的&#xff1b;override和overwrite的翻译&#xff0c;我在参考了cppreference中…

.NET周刊【5月第2期 2024-05-12】

国内文章 C#在工业数字孪生中的开发路线实践 https://mp.weixin.qq.com/s/b_Pjt2oii0Xa_sZp_9wYWg 这篇文章探讨了C#在工业数字孪生技术中的应用&#xff0c;介绍了三种基于C#的数字孪生系统实现方案&#xff1a; WPF Unity&#xff1a;结合WPF技术和Unity引擎&#xff0c…

接口、会话控制

文章目录 接口介绍RESTful APIjson-server接口测试工具apipost公共参数和文档功能 会话控制cookie介绍和使用运行流程浏览器中操作Cookieexpress中cookie操作 Sessionsession运行流程&#xff1a;session中间件配置session 和 cookie 的区别CSRF跨站请求伪造 tokenJWT介绍与演示…

idea控制台日志控制

1.清除控制台log日志 测试的时候&#xff0c;控制台打印的日志比较多&#xff0c;速度有点慢而且不利于查看运行结果&#xff0c;所以接下来我们把这个日志处理下: 取消初始化spring日志打印&#xff0c;resources目录下添加logback.xml&#xff0c;名称固定&#xff0c;内容如…

vscode 通过ssh 远程执行ipynb +可以切换conda env

主要是保证几个点 远程服务器python 环境没问题 conda这些也都有的ssh的账户 是有conda权限的没有免密就输入密码 免密教程就是最基本的那种 公钥copy过去就行了vscode 那几个插件都要装好 开始操作 首先 vscode 点击左侧工具栏中的扩展&#xff0c;搜索“ssh”&#xff0c;…

【IC前端虚拟项目】验证环境env与base_teat思路与编写

【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 上一篇里解决了最难搞的axi_ram_model,接下来呢就会简单又常规一些了,比如这一篇要说的env和base_test的搭建。在这里我用了gen_uvm_tb脚本: 【前端验证】验证自动化脚本的最后一块拼图补全——gen_t…

怎么得到所有大写字母/小写字母组成的字符串

有时候&#xff0c;可能需要获取a~z、A~Z组成的26个字母的字符串&#xff0c;这篇文章介绍一种简单的方法。 只需要几句简单到不能再简单的代码&#xff01;你不会还在傻傻地一个个字母敲吧~ /*** author heyunlin* version 1.0*/ public class Example {/*** 小写字母*/priv…

游戏数值策划关卡策划文案策划系统策划及游戏运营干货

1.《游戏新手村》免费电子书 我2007年开始做网络游戏&#xff0c;后面又做过网页游戏和手机游戏。当时市面上关于游戏策划和运营的书籍屈指可数&#xff0c;于是我就想着要不我写一本吧&#xff0c;然后2014年10月开始撰写。关于本书的更多信息可查看这篇文章>> 游戏新手…

四川景源畅信:抖音有哪些可以做的副业?

抖音作为当前最受欢迎的短视频平台之一&#xff0c;其巨大的流量和用户基础为许多人提供了副业的机会。那么&#xff0c;在抖音上可以做哪些副业呢? 一、内容创作与推广 利用抖音平台进行内容创作是最直接的副业方式。无论是搞笑短剧、生活分享还是专业知识普及&#xff0c;只…

激光雷达在高速公路车型识别中的创新应用

一、引言 高速公路作为现代交通体系的重要组成部分&#xff0c;其安全性和管理效率对保障人民生命财产安全具有重要意义。在高速公路管理中&#xff0c;车型识别是实现交通流量分析、车辆分类管理等功能的基础。近年来&#xff0c;激光雷达技术以其高精度、高分辨率和全天候工…

Notepad8.1.9汉化中文版

下载地址&#xff1a;Notepad8.1.9.zip 一款优秀免费的源代码编辑器&#xff0c;支持语法高亮的开源纯文本编辑器&#xff0c;并且可以替代系统自带的记事本的功能。

CSS表格特殊样式

列组样式 使用colgroup与col标签配合可以定义列祖样式&#xff1a;例 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>table,tr,th,td{border: 1px solid #000;}table{border-collapse: coll…

全方位入门git-慕课网 笔记

目录 【上传github忽略某些文件】【配置用户名和邮箱】【想要删除不需要的文件时如何进行操作】【想要给文件重命名如何操作】【想要移动文件到其他位置时如何操作】【文件有变化时&#xff0c;如何查看前后变化】【操作失误的情况下如何实现一键还原】【不再追踪时如何实现撤销…

接口测试基础

1、接口测试 接口&#xff1a;系统之间数据交互的通道。 硬件接口软件接口 接口测试&#xff1a;基于不同的输入参数&#xff0c;校验接口响应数据与预期数据是否一致。 接口地址 接口参数 2. 为什么要学接口测试&#xff1f; 提前介入测试、尽早发现问题 3、接口测试学什…

猫狗分类识别①将文件夹中图像统一转化为某一固定格式

一、 导入库 pip install Image 二、确保库中存在Image库&#xff0c;可以在Terminal或控制台中查看 pip list 三、图像后缀处理 import os from PIL import Image def convert_to_png(input_folder, output_folder):if not os.path.exists(output_folder):os.makedirs(outp…

机器学习笔记 PostgresML教程:使用SQL进行机器学习

机器学习的基本做法是将数据转移到模型的环境中进行训练。由于今天的数据库比机器学习模型大好多个数量级,所以PostgresML的思路是,如果我们将模型引入数据集不是会容易得多吗? PostgresML 是一个建立在流行的 PostgreSQL 数据库之上的综合机器学习平台。它引入了一种称为“…

最新极空间部署iCloudpd教程,实现自动同步iCloud照片到NAS硬盘

【iPhone福利】最新极空间部署iCloudpd教程&#xff0c;实现自动同步iCloud照片到NAS硬盘 哈喽小伙伴们好&#xff0c;我是Stark-C~ 我记得我前年的时候发过一篇群晖使用Docker部署iCloudpd容器来实现自动同步iCloud照片的教程&#xff0c;当时热度还很高&#xff0c;可见大家…

二分答案(区间范围)

D. Jumping Through Segments 输入数据 4 5 1 5 3 4 5 6 8 10 0 1 3 0 2 0 1 0 3 3 3 8 10 18 6 11 4 10 20 0 5 15 17 2 2 输出范围 7 0 5 13#include<bits/stdc.h> #define int long long using namespace std; typedef pair<char,int>PII; const int N2e510;…

测试和开发工作必备的17个Python自动化代码

您是否厌倦了在日常工作中做那些重复性的任务&#xff1f;简单但多功能的Python脚本可以解决您的问题。 我们将通过上下两个篇章为您介绍17个能够自动执行各种任务并提高工作效率Python脚本及其代码。无论您是开发人员、数据分析师&#xff0c;还是只是希望简化工作流程的人&…

解决webstorm没有vue语法提示;webstorm没有代码提示

解决webstorm没有vue语法提示&#xff1b;webstorm没有代码提示 使用webstorm 2023.x 开发vue项目。发现死活没有vue语法提示&#xff0c;即便是npm install、清理缓存。对比其他vue项目却有语法提示&#xff0c;最后发现依赖库被忽略了&#xff1a; 删除掉node_modules 的忽略…