使用C语言在PIC18上进行多线程编程

9

在没有操作系统的情况下,如何创建能够并行运行的线程来编程PIC18微控制器?


你使用的编译器是什么?有些编译器具有此目的的功能。 - Jeanne Pindar
可以使用计时器中断或其他基于事件的中断等技巧,在没有操作系统的情况下实现分离的执行上下文,但是线程:在没有操作系统的情况下不可能实现。 - Alphaneo
您使用低占用的实时操作系统(RTOS)。 - Kortuk
这个问题非常开放性。 - J. Polfer
13个回答

12

不要使用线程,而应该使用事件循环。

PIC18是一个小型处理器,基于事件循环的风格意味着您不必保留许多深度堆栈。您需要按照事件循环的方式编写代码,但这可能是合理的。

如果您有一些长时间运行的任务,请使用定时器和不同的中断优先级来允许更高优先级的事件循环抢占较低优先级的事件循环,并将适当类型的工作放入适当的事件队列中。


1
这是一个PIC18芯片,线程需要在某个地方实现并且它们的状态需要被存储。你可以以异步方式完成几乎所有的I/O操作;如果你开始进行同步I/O操作,为了可靠性必须禁用中断,那么你也不会被抢占。你可以在芯片的异步特性上实现一个线程库和调度程序,但为什么要费心呢?你可能只有不到4kB的RAM,并且时间关键的操作很可能是I/O驱动的。只需将PIC18视为硬件实现的非常简单的操作系统即可。 - janm
1
我理解为什么人们说这不是你会做的事情,但使用RTOS来隔离线程大大提高了编程质量。在复杂的安全关键代码中,一个RTOS可以为一组工程师节省数月时间,这可以超过6位数的储蓄,使用经过安全认证的RTOS。 - Kortuk
@Kortuk 没问题,我知道那不是你的意图。我同意解耦是好的,但你可以将解耦需求与RTOS分离!选择RTOS取决于许多因素,包括开发人员的经验、要解决的问题、时间、金钱等。关注点分离是好的开发者考虑并在他们自己的代码中执行的事情,而不仅仅是跨第三方代码的边界。 - janm
@janm,说到这一点,我认为用户已经选择他们想要进行线程操作,我认为帮助他们进入线程操作是有价值的。现在我遇到过很多人真的以为线程操作就像在 pic18 上调用 fork()一样。 - Kortuk
@janm,如果提问者接受了答案,你就能知道他们想要什么。感谢讨论,我很喜欢它。 - Kortuk
显示剩余7条评论

9
你可以尝试使用协作式多任务处理
对于PIC解决的问题类型,如果你尝试使用中断或轮询而不是多线程,则可能会更好。

我刚刚发布了一些代码,以说明协作式多任务处理可以有多简单。虽然我没有在PIC18上完成过这个任务,但我已经在8051和TI DSP平台上完成过。 - supercat

7

5
如果没有操作系统,你需要自己重新创建必要的��能。最简单的方法可能是安装一个定时器中断,以某个适当的频率运行(可能取决于实际时钟速度,但可能在100-1000 Hz范围内)。在中断处理程序中,您需要检查当前线程的状态,并决定是否应该进行切换。关键是在必要时进行切换,并从中断处理程序返回到不同的线程。当线程本身可能使用中断时,使其正常工作并不容易。您还可以尝试安装一些内核,例如Contiki。这里是PIC18的“protothreads”的示例Here,看起来代码量合理。不过语义方面就不确定了。 更新:这可能需要您编写一些汇编语言的最低级代码(我不确定,在PIC上没有用C工作过,所以我不知道您可以获得多少控制权)。您需要控制程序计数器的寄存器,而这些不是C语言的概念。

2
请注意,在微控制器上,一些“线程”也可以由特定的中断处理程序处理,并因此在“并行”运行于您的主事件循环中。
例如,如果您有一个外部事件触发ADC转换,则ADC转换完成的处理程序可以获取该值,进行一些计算,然后设置一些输出位以根据ADC值调整控制输出。所有这些都可以在中断处理程序中发生,并因此与其他所有内容并行运行。
根据您需要并行执行的任务,您可以选择多种技术组合来使并行工作按预期运行。

2

1
我想分享一下我在C编程语言中编写的微控制器任务管理小内核。您可以创建一个带有周期、挂起、恢复和随时更改任务周期的任务。该内核可以删除所有任务以创建另一个序列器,以满足您的需求。 内核提供了TickGet函数来管理所有定时器,以满足您的需求。 您只需要创建一个中断函数并替换Timer()函数即可获得适用于自己应用程序的微内核。 该内核基于循环链表来恢复,以在任务之间切换。它是以通用方式编写的,以帮助人们为其自己的应用程序进行定制。像轮询任务调度一样,任务之间没有优先级。我根据MISRA指南(汽车规范)编写了此源代码。 您可以在这里下载它。
我希望能够帮助人们管理微控制器上的任务。

1

8051上,我使用了简单的堆栈切换器实现了双重任务处理。我相信在PIC上也可以做到,只要每个任务只使用16级堆栈。以下是代码示例(假设_altSP在公共bank中):

_InitTask2:
    movff _STKPTR,_altSP
    movlw 16
    movwf _STKPTR,c
    goto _Task2Start
_TaskSwitch: movf _altSP,w,c movff _STKPTR,_altSP movwf _STKPTR,c return

主任务应该调用_InitTask2来启动第二个任务。第二个任务将一直运行,直到调用_TaskSwitch,此时主任务将恢复执行并继续从调用_InitTask2的指令处执行。以后,每次任务调用_TaskSwitch时,另一个任务将从上一次调用_TaskSwitch的位置继续执行。

如果您采用这种方法,请通知编译器所有寄存器可能会被_InitTask2或_TaskSwitch的调用清空。还必须告诉它_Task2Start和它所调用的函数必须从主任务分配独立的变量空间。

我不确定你需要告诉编译器什么才能让它满意,但我可以说协作式双任务可以使一些事情运行得非常顺畅。


这份应用笔记指出,如果核心不是PIC18,则无法访问堆栈指针。我不懂汇编语言,正在尝试学习简单的堆栈切换器。这是否意味着我希望使用PIC12和PIC16创建一个简单的任务切换器是无稽之谈? - abdullah kahraman
1
@abdullahkahraman:在PIC12或PIC16上同时执行代码的唯一方法是要求只有其中一个任务可以在嵌套子程序中执行任务切换。顺便说一句,我曾经在基于PIC165x芯片的一个很好的任务切换应用程序中工作过,其中两个任务几乎相同,除了I/O分配。每个任务都使用一组存储器。效果非常不错。 - supercat

1

CCS编译器包含一个RTOS。虽然我没有使用过,但从编译器手册中可以了解到:

CCS实时操作系统(RTOS)允许PIC微控制器定期运行任务而无需中断。这是通过一个函数(RTOS_RUN())来实现的,该函数充当调度程序。当安排要运行的任务时,调度函数将处理器的控制权交给该任务。当任务执行完成或不再需要处理器时,处理器的控制权将返回到调度函数,然后将在适当的时间将处理器的控制权交给下一个要执行的任务。这个过程被称为协作式多任务处理。

只是提醒一句 - 检查他们的论坛以获取有关您正在寻找的特定功能的信息。显然,CCS有一个习惯,在完全测试之前发布新功能。这就是我仍在使用旧版本(v3.249)的原因之一。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接