如果之前您做过无操作系统的嵌入式开发,使用“前台main——后台中断”这种前后台模式开发。
例如,任务1,你要完成接收串口来的1000字节数据,接收完毕数据后,进行校验,校验正确后,把所有数据乘以100,再从串口将数据发送(暂不考虑异常时处理);任务2,你要完成从网口接收1000字节数据,进行校验,校验正确后,把所有数据乘以100,再从网口将数据发送(暂不考虑异常时处理)。
对于上述要求的任务,常规逻辑就是:在串口中断中,将接收到的数据放入数据缓冲区中,当接收完毕1000字节后,置串口数据接收完毕标志,程序代码main中的while(1)循环中查询串口接收标志有效,怎进行数据的校验和乘以100的处理;同理,网络接收数据也是这样处理,中断中接收数据、置标志,主循环中数据处理。C语言伪代码如下:
//串口中断处理函数
void uart_irq(void){
//从串口寄存器中读数据,并放在uart_data[]缓冲区中
uart_data[uart_data_len] = uart_rev_register;
uart_data_len ++;
//接收到UART_REV_LEN(当前要求为1000字节),则置标志有效
if(uart_data_len >= UART_REV_LEN){
uart_rev_flag = TRUE;
uart_data_len = 0;
}
}
//网口中断处理函数
void net_irq(void){
//从网口寄存器中读取数据,并放在net_data缓冲区中
for(temp_i = 0; temp_i < net_len_reg; temp_i ++){
net_data[net_data_len] = net_data_register;
net_data_len ++;
}
//则置标志有效
net_rev_flag = TRUE;
uart_data_len = 0;
}
void main(void){
while(1){
//串口接收标志有效
if(uart_rev_flag == TRUE){
//置标志为无效,用于下次数据接受
uart_rev_flag == FALSE;
//调用串口数据处理函数
uart_data_process_func();
}
//网口接收标志有效
if(net_rev_flag == TRUE){
//置标志为无效,用于下次数据接受
net_rev_flag == FALSE;
//调用网口数据处理函数
net_data_process_func();
}
}
}
如果你的项目要求20个任务,那你的main会很庞大,但也能做;如果,部分任务之前有任务的紧急程度不同,那用这种“前后模式”设计程序,非常考研设计的任务规划能力,再加上后续用户不断的新增新的任务,那经过若干次修改后,程序简直不忍直视。
不要小看了用户修改,我有个项目,历经5年时间,修改了无数的版本;要记住一句忠告“用户虐我千百遍,我待用户如初恋”,用户永远是上帝。
必备工具:请将NXEZ瑞士军刀开发板,安装到树莓派上,如下所示
设计任务:任务1,LED灯按照流水灯方式进行点亮;任务2,数码管显示程序运行的时间,单位:秒。任务和简单吧,你可以想想用前后台模式怎么设计,是不是要开个定时器,在主循环中一会变led的状态,一会变数码管的状态,好费经吧。
那让我们借助操作系统的多任务这个工具吧;看吧,是不是很简单:)。
from multiprocessing import Process
import time
import os
from datetime import datetime
from sakshat import SAKSHAT
from sakspins import SAKSPins as PINS
#流水灯每个灯点亮时间
WATERLIGHT_DELAY = 0.1
#数码管刷新显示数字的延时
DIGSEC_DELAY = 0.7
#Declare the SAKS Board
SAKS = SAKSHAT()
#流水灯任务定义
def waterlight(task_name, delay_ts):
print(task_name + "任务启动")
try:
while True:
for i in range(0,8):
SAKS.ledrow.on_for_index(i)
time.sleep(delay_ts)
SAKS.ledrow.off_for_index(i)
except KeyboardInterrupt:
print(task_name + "任务被终止")
#数码管显示秒值
def digital_second(task_name, delay_ts):
print(task_name + "任务启动")
#记录开始时间
start_time = datetime.utcnow()
try:
while True:
end_time = datetime.utcnow()
c = end_time - start_time
#print c.seconds
#print c.microseconds
SAKS.digital_display.show(("%02d.%02d" % (c.seconds, c.microseconds)))
time.sleep(delay_ts)
except KeyboardInterrupt:
print(task_name + "任务被终止")
if __name__ == "__main__":
try:
#创建led和数码管显示闪烁任务
led_flash = Process(target=waterlight, args=("流水灯", WATERLIGHT_DELAY))
digsec = Process(target=digital_second, args=("数码管显示", DIGSEC_DELAY))
# 启动任务
led_flash.start()
digsec.start()
#等待启动的进程执行结束
led_flash.join()
digsecjoin()
except KeyboardInterrupt:
print("任务被终止了")
python提供了os.fork创建进程,提供了thread模块创建线程,这不是足够了吗?
这是不够的,os.fork无法在windows实现,不具有可移植性;线程虽然可以,但由于python的GIL(Global Interpreter Lock,全局解释器锁)的存在,同一时刻python只能运行一个线程,没法将多线程分配到多核上运行。
这就是引入了multiprocessing模块的原因,解决了windows移植到问题和如何利用多核的问题。
如何使用multiprocessing模块呢?就3步。
1.定义自己的任务,
#流水灯任务定义
def waterlight(task_name, delay_ts):
print(task_name + "任务启动")
try:
while True:
for i in range(0,8):
SAKS.ledrow.on_for_index(i)
time.sleep(delay_ts)
SAKS.ledrow.off_for_index(i)
except KeyboardInterrupt:
print(task_name + "任务被终止")
2.将任务交给multiprocess模块,
#创建led闪烁任务
3.启动该任务。
# 启动任务
看吧,就3步,是不是很简单。
从大的方面来说,进程在运行过程中有3种状态:就绪态、运行态、阻塞态。
(1)就绪态。
就绪态:进程具备运行条件了,可以被cpu执行了;但还在就绪队列中排着队。
例如,我们led灯例程中有两个地方是就就绪态的。
1.进程名.start()执行后,进程进入就绪态了。
2.time.sleep()之后,点亮灯(或熄灭灯)之前,进程进入就绪态了。
(2)运行态。
运行态:进程就是在cpu上运行了。
例如,执行把运行灯操作语句,把灯点亮(或者熄灭)时,这是就是运行态。
(3)阻塞态。
阻塞态:进程在等待某个事件发生的过程中,就是阻塞态;此时,一直在阻塞队列中等待着。
例如,执行time.sleep语句,进行延时时。
多任务好吧,你把做的事情告送操作系统,操作系统帮你进行管理、调度。
对比下,multiprocessing模块和thread模块的效率
下属代码及结论,摘抄自“莫烦python”, 链接:https://morvanzhou.github.io/tutorials/python-basic/multiprocessing/4-comparison/
结论:发现多核/多进程最快,说明在同时间运行了多个任务。 而多线程的运行时间居然比什么都不做的程序还要慢一点,说明多线程还是有一定的短板的。
更多关于multiprocessing的介绍运行下面代码
本课程不是python语言语法学习课程,因为现在网上有大量优质的python语言语法学习课程,同学们可以自行选择免费的或收费的python语法课程,学习python本身。
本课程定位于:如何使用python理解树莓派、理解操作系统。放心由于python语言的通俗易懂,加上本课程都是最基本的python,及时没有基础,也可以快速理解,记住,咱们的课程是可以边学边练、随意修改、还有恢复机制,放心折腾吧。
咱们不探究python语言本身,那咱们探究下何为编程语言。(下面是个人理解,咱们可以敞开讨论)
编程语言的目的就是——把我们要做成的事情,翻译成计算机能理解的方式,告送计算机,由计算机帮我们实现。那咱们看看我总结的编程语言三大组成部分:
(1)变量。就是咱们要处理的信息,例如:温度数据、车的速度,楼的高度。变量可以继续划分,但划分的目的就是存储数据的大小,例如8位(2的8次方)变量可以存储0~255数字量,32位数据(2的32次方)存储0~4294967295。还有16位,64位、或者像python中不限制大小的变量。
(2)程序结构。就是咱们处理信息所使用的流程。有顺序结构、条件结构、循环结构。顺序结构很容易理解,就是依次处理数据,例如,我们依次进行,获取温度数据、获取汽车速度;条件结构,就是需要对信息进行判断,然后再处理,例如,获取完毕汽车速度后,判断汽车超速,就进行刹车;循环结构,就是不断地重复相同的事情,例如,我们每隔1s采集一次温度,就是不断循环的进行采集温度、睡眠1s、再采集温度、睡眠1s…。
(3)系统和第三方提供的库。其实有了变量和程序结构,就可以完成咱们要的工作,但是如果事必躬亲的话,估计咱们还处在原始社会。后人进步总是站在前人的肩膀上继续攀登。所以,前人已经他们已完成的工作,打包好,前人已完成的功能,我们理解后直接用,我们专注于自己新的工作。系统和第三方库,就是我们可以使用的现成工具,我们合理利用这些工具,重新组合完成我们新的需求,如果您做的好,您可以将您的工作封装成第三方库,留给后人使用。
这是我理解的编程语言的框架,您可以先按照我的方式看语言,或者自己建立自己语言框架。
课程 bilibili 视频地址:https://www.bilibili.com/video/av71878718/?p=10
导轨又称滑轨、线性导轨、线性滑轨,用于直线往复运动场合,拥有比直线轴承更高的额定负载, 同时可以承担一定的扭矩,可在高负载的情况下实现高精度的直线运动。
Python 是一门简单易学且功能强大的编程语言。
Pillow在PIL的基础上,为Python3增加了更多功能和支持。我们将看到如何在图像上执行各种操作,例如裁剪,调整大小,添加文本到图像,旋转,灰阶转换。
这11项比赛都是与STEAM科创项目相关,如果积极参与、甚至获奖势必对孩子在科创项目学习、未来升学、培养人工智能时代人才等方面打下坚实基础。
Blockly Games 是为没有计算机编程经验的孩子们设计的一系列学习编程的游戏。