完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
一、小型硬件系统固件该怎么写?
在没有嵌入式操作系统的时候,对于任务稍微多一点的硬件系统,比如这样一个小系统 任务包括: 1、模拟信号采集(使用MCU的ADC,或者专用的信号前端使用SPI/I2C通信) 2、数字信号处理(有各种各样的算法) 3、数据保存(SPI的Nandflash) 4、交互(蓝牙、USB) 5、调试打印(UART/RTT) 对于我来说就是中断+状态机
中断+状态机 我们可以没有任何延时函数,遇到延时就改变状态,等到下次循环到了再处理这种状态来实现延时,其他时间就让系统进入休眠。这样的模式对于低功耗系统,特别是穿戴领域非常重要,想想又提高电池续航了 二、状态机越来越复杂? 利用状态机确实能够保证不用暴力延时了(比如循环计数100000次代表1ms延时),但是有没有想过,有些情况状态会越来越复杂。 以flash驱动为例:
在这个时候想要是不用状态机,一件事情按顺序完成不就好了,简单明了好维护。但是想想这样就又回到了利用延时来做,单一任务倒是没问题,其他任务怎么办。正好嵌入式操作系统可以解决呀。
对于一般低功耗产品倒是可以基本接受,但是在穿戴领域和高计算量的情况,已经到了扣指令,恨不得C语言都不要用了直接用汇编的地步,这就成了困扰。 这个时候就在想有没有什么办法既可以让驱动单任务一直运行,又可以抛弃嵌入式操作系统的方法呢? 其实也可以实现,只要借鉴先嵌入式操作系统的实现原理,然后自己实现个任务切换功能就好了,至于其他的什么内存管理啊,任务调度算法什么的对于这个小小的硬件系统,真心用不上。 原理很简单,只要会一点汇编就OK了,利用一小小片内存:
[tr]函数说明[/tr]
四、举个例子,将数据从flash的缓存中读到MCU的函数 int NandFlash_read_from_cache(uint32_t address, uint8_t *buffer, uint32_t len) { //发送读取命令,也就是发送参数给硬件SPI器件 len = spi_read(address, buffer, len); //进入挂起函数,等待完成 suspendModule(STACK_SUSPEND); //到这里时一次读操作就已经完成了 return len; } 这里的suspendModule函数应该怎么实现呢?
void suspendModule(stackStatus_t status) { while(true) { if(SPI->status|SPI_SEND_OK) break; } } 显然这样会一直占用CPU,无法低功耗,而且其他任务也不能运行。
void suspendModule(stackStatus_t status) { while(true) { if(SPI->status|SPI_SEND_OK) break; vTaskDelay(1); } } 这里vTaskDelay虽然也是延时,但是操作系统实际是切换任务去做其事情了,不会一直占用CPU。
void SPI_IRQHandler(void) { static BaseType_t xHigherPriorityTaskWoken; xHigherPriorityTaskWoken = pdFALSE; // Unblock the task by releasing the semaphore. xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken ); portEND_SWITCHING_ISR(xHigherPriorityTaskWoken); } void suspendModule(stackStatus_t status) { xSemaphoreTake(xSemaphore,portMAX_DELAY); }
void SPI_IRQHandler(void) { resumModule(); } int NandFlash_read_from_cache(uint32_t address, uint8_t *buffer, uint32_t num) { //发送读取命令,也就是发送参数给硬件SPI器件 int len = spi_read(address, buffer, num); //进入挂起函数,等待完成 suspendModule(STACK_SUSPEND); //到这里时一次读操作就已经完成了 return len; } __asm void suspendModule(stackStatus_t status) { extern origin_sp; extern stack_index; extern stackStatus; extern driver_stack; PRESERVE8 LDR R1,=stackStatus STRB R0,[R1] LDR R1,=origin_sp //读取原始堆栈指针 LDR R0,[R1] MOV R1,SP SUB R0,R0,R1 LDR R1,=stack_index //需要备份的堆栈字节数 STRB R0,[R1] //保存现场 LDR R1,=driver_stack ADD R0,R1,R0 STMIA R1!, {R4-R12,LR} //子程序中的10个通用寄存器备份 B suspendStart suspendStatck POP {R2} STR R2,[R1,#4]! suspendStart CMP R0,R1 BGT suspendStatck //返回 DSB ISB POP {R4-R12,PC} //恢复之间入栈的通用寄存器 ALIGN } __asm void resumModule(void) { extern origin_sp; extern stack_index; extern driver_stack; PRESERVE8 LDR R1,=origin_sp STR sp,[R1] PUSH {R4-R12,LR} LDR R1,=stack_index LDRB R0,[R1] CBZ R0,jobExit1 //恢复现场 LDR R1,=driver_stack ADD R2,R1,R0 LDMIA R1!, {R4-R12,LR} B resumStart resumStatck LDR R0,[R2] PUSH {R0} SUB R2,R2,#4 resumStart CMP R2,R1 BGT resumStatck DSB ISB BX LR jobExit1 POP {R4-R12,PC} ALIGN } 函数 suspendModule 做的就是将相关寄存器存入内存中,然后去完成其他任务。 函数resumModule做的就是在完成操作中断来临时,恢复相关寄存器,继续完成NandFlash_read_from_cache剩下的事。 可以看到,对于函数NandFlash_read_from_cache来说就是一次操作,使用者也不用考虑独占CPU其他任务不能使用的问题了。 |
|
|
|
只有小组成员才能发言,加入小组>>
895 浏览 0 评论
1202 浏览 1 评论
2579 浏览 5 评论
2914 浏览 9 评论
移植了freeRTOS到STMf103之后显示没有定义的原因?
2776 浏览 6 评论
keil5中manage run-time environment怎么是灰色,不可以操作吗?
1251浏览 3评论
228浏览 2评论
503浏览 2评论
410浏览 2评论
M0518 PWM的电压输出只有2V左右,没有3.3V是怎么回事?
489浏览 1评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-2-3 21:23 , Processed in 0.951065 second(s), Total 47, Slave 38 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号