齐护K210系列教程(十四)_录像存SD卡


AIstart可以录像并存在SD卡中,格式跟播放的格式一样为:avi

但录像时暂不支持同时录音,这点要注意。

1,名词科普

1-1 帧率(Frame rate)

是以帧称为单位的位图图像连续出现在显示器上的频率(速率)。该术语同样适用于胶片和摄像机,计算机图形和动作捕捉系统。帧速率也可以称为帧频率,并以赫兹(Hz)表示。

帧率和人类视觉 人类视觉的时间敏感度和分辨率根据视觉刺激的类型和特征而变化,并且在个体之间不同。人类视觉系统每秒可处理10到12个图像并单独感知它们,而较高的速率则被视为运动。当速率高于50 Hz至90 Hz时,大多数研究参与者认为调制光(如计算机显示器)稳定。这种调制光的稳定感被称为闪烁融合阈值。然而,当调制光是不均匀的并且包含图像时,闪烁融合阈值可以高得多,数百赫兹。

[1] 关于图像识别,已经发现人们在不间断的一系列不同图像中识别特定图像,每个图像持续少至13毫秒。

[2] 视力的持久性有时会导致非常短的单毫秒视觉刺激,其感知持续时间在100毫秒到400毫秒之间。非常短的多个刺激有时被认为是单个刺激,例如10毫秒的绿色闪光,紧接着是10毫秒的红色闪光,被感知为单个黄色闪光。

而对游戏,一般来说,第一人称射击游戏比较注重FPS的高低,如果FPS<30的话,游戏会显得不连贯。所以有一句有趣的话:“FPS(指FPS游戏)重在FPS(指帧率)。 每秒的帧数(fps)或者说帧率表示图形处理器处理场时每秒钟能够更新的次数。高的帧率可以得到更流畅、更逼真的动画。一般来说30fps就是可以接受的,但是将性能提升至60fps则可以明显提升交互感和逼真感,但是一般来说超过75fps一般就不容易察觉到有明显的流畅度提升了。如果帧率超过屏幕刷新率只会浪费图形处理的能力,因为监视器不能以这么快的速度更新,这样超过刷新率的帧率就浪费掉了。

1-2 采样率:

在科学领域,常用的采样率有: 30 Hz (30 fps) - 普通摄像机

/** * @file packet_portting.c * @author Lu Yongping (Lucas@hiwonder.com) * @brief 串口协议接口实现 * @version 0.1 * @date 2023-05-23 * * @copyright Copyright (c) 2023 * */ #include "global.h" #include "lwrb.h" #include "usart.h" #include "k210_packet.h" #include "cmsis_os2.h" #include <stdio.h> #include "lwmem_porting.h" #include "k210_packet_handle.h" #define K210_RX_FIFO_BUFFER_SIZE 2048 /* FIFO缓长度 */ #define K210_RX_DMA_BUFFER_SIZE 256 /* 单个DMA缓长度 */ /* 对外暴露的变量 */ struct k210_PacketController k210_packet_controller; /* 协议控制器实例 */ /* 内部函数 */ void k210_dma_receive_event_callback(UART_HandleTypeDef *huart, uint16_t length); /* 串口控制依赖的外部变量 */ extern osSemaphoreId_t k210_rx_not_emptyHandle; #if 1 /** * @brief 初始化packet 控制器对象 * @retval void */ void k210_packet_init(void) { memset(&k210_packet_controller, 0, sizeof(k210_packet_controller)); k210_packet_controller.state = K210_CONTROLLER_STATE_STARTBYTE1; k210_packet_controller.data_index = 0; /* DMA 接收缓初始化 */ static uint8_t rx_dma_buffer1[K210_RX_DMA_BUFFER_SIZE]; static uint8_t rx_dma_buffer2[K210_RX_DMA_BUFFER_SIZE]; k210_packet_controller.rx_dma_buffers[0] = rx_dma_buffer1; k210_packet_controller.rx_dma_buffers[1] = rx_dma_buffer2; k210_packet_controller.rx_dma_buffer_size = K210_RX_DMA_BUFFER_SIZE; k210_packet_controller.rx_dma_buffer_index = 0; /* 接收 FIFO 初始化 */ k210_packet_controller.rx_fifo_buffer = LWMEM_CCM_MALLOC(K210_RX_FIFO_BUFFER_SIZE); k210_packet_controller.rx_fifo = LWMEM_CCM_MALLOC(sizeof(lwrb_t)); lwrb_init(k210_packet_controller.rx_fifo, k210_packet_controller.rx_fifo_buffer, K210_RX_FIFO_BUFFER_SIZE); } /** * @brief 开始串口协议接收 * @retval void */ void k210_start_recv(void) { HAL_UART_RegisterRxEventCallback(&huart2, k210_dma_receive_event_callback); /* 注册接收事件回调 */ /* 使用 ReceiveToIdle_DMA 进行接收, 该函数会在DMA缓满时中断或在接收空闲时中断并触发接收事件回调 */ HAL_UARTEx_ReceiveToIdle_DMA(&huart2, k210_packet_controller.rx_dma_buffers[k210_packet_controller.rx_dma_buffer_index], K210_RX_DMA_BUFFER_SIZE); /* 开始接收 */ } /** * @brief 串口协议包接收事件回调 * 在 DMA 接收缓满后或者空闲时触发本函数将接收到的数据压入接收FIFO缓,再由接收任务完成解析和处理 * @param huart 串口实例 * @param Pos 接收到的字节数 * @retval void */ void k210_dma_receive_event_callback(UART_HandleTypeDef *huart, uint16_t length) { int cur_index = k210_packet_controller.rx_dma_buffer_index; /* 取得当前DMA缓的索引号 */ k210_packet_controller.rx_dma_buffer_index ^= 1; if(length < K210_RX_DMA_BUFFER_SIZE) { HAL_UART_AbortReceive(&huart2); } HAL_UARTEx_ReceiveToIdle_DMA(&huart2, k210_packet_controller.rx_dma_buffers[k210_packet_controller.rx_dma_buffer_index], K210_RX_DMA_BUFFER_SIZE); lwrb_write(k210_packet_controller.rx_fifo, k210_packet_controller.rx_dma_buffers[cur_index], length); /* 将接收到的数据写入fifo ring */ printf("queue rec\n"); osSemaphoreRelease(k210_rx_not_emptyHandle); /* 置位接收缓非空信号 */ } /** * @brief 串口协议包的接收任务入口 * 等待串口缓非空信号量,然后从串口接收FIFO缓中取出数据进行解析并处理 * 串口缓非空信号量由串口接收事件置位 * @param argument 保留 * @retval void */ void k210_rx_task_entry(void *argument) { osSemaphoreAcquire(k210_rx_not_emptyHandle, osWaitForever); /* 默认信号不为零,先清除掉 */ __HAL_UNLOCK(&huart2); //注册回调函数 k210_packet_handle_init(); //开始接收 k210_start_recv(); printf("k210_rx_task_entry\n"); for(;;) { osSemaphoreAcquire(k210_rx_not_emptyHandle, osWaitForever); /* 等待接收缓非空 */ printf("da\n"); k210_packet_recv(&k210_packet_controller); /* 处理接收数据 */ } } #endif
03-27
<think>好的,我现在需要分析用户提供的这段C代码,并按照他们的要求进行介绍。首先,用户给了一个关于串口协议接口实现的代码文件packet_portting.c,希望我用中文清晰解释这段代码的结构和功能。 首先,我要通读代码,理解各个部分的作用。代码中包含了头文件、宏定义、全局变量声明以及几个函数的实现。看起来主要功能是处理串口数据接收,使用DMA和FIFO缓冲区来高效处理数据,并结合RTOS进行任务管理。 接下来,用户要求按照特定的格式回答,包括使用行内数学表达式和独立公式,不过在这个代码解释的场景下可能用不到数学公式,但需要确保LaTeX语法正确。不过可能代码中的某些部分需要强调,比如宏定义的值或者函数名,可以用行内代码块标记,而不是数学公式。不过用户特别指出所有行内数学表达式必须用$...$,可能需要确认是否有变量或方程需要如此表示,但代码解释可能较少涉及数学内容,可能需要灵活处理。 然后,用户的结构要求是分步骤解释,帮助用户逐步理解。需要将代码分解为初始化、启动接收、回调函数、任务入口等部分,分别解释每个部分的功能和实现细节。 需要注意代码中的关键点:比如使用DMA双缓冲区接收数据,利用lwrb库实现FIFO,以及通过RTOS的信号量来同步任务。需要解释这些技术如何协同工作,提高数据接收的效率和可靠性。 另外,用户可能关心代码中的某些细节,比如为什么使用两个DMA缓冲区,如何避免数据溢出,以及如何处理接收完成的中断事件。需要确保解释清晰,避免技术术语过多,但又要准确。 在解释过程中,可能会遇到需要说明函数调用流程的地方,比如k210_start_recv函数中注册回调并启动DMA接收,然后在回调函数中将数据写入FIFO,并释放信号量,触发任务处理数据。这个流程需要分步骤说明,确保逻辑连贯。 同时,需要注意代码中使用的库和外部依赖,比如lwrb、CMSIS OS2接口,HAL库函数等,可能需要简要提及这些库的作用,但不需要深入,除非用户特别要求。 最后,要确保整个回答结构清晰,按照代码的结构逐部分解释,并且符合用户的中文回答要求,避免使用Markdown格式,但保持段落和逻辑顺序。需要检查是否有遗漏的重要部分,比如全局变量k210_packet_controller的结构,或者可能的内管理部分(如LWMEM_CCM_MALLOC的使用)。 可能还需要指出代码的一些潜在问题或优化点,比如DMA缓冲区大小选择的原因,或者FIFO缓冲区的线程安全性,但用户可能只是需要介绍现有代码,不一定需要优化建议,除非用户特别指出。 总结来说,回答的结构应该包括: 1. 概述代码的整体功能和结构 2. 初始化函数k210_packet_init的作用 3. 启动接收函数k210_start_recv的流程 4. 回调函数k210_dma_receive_event_callback的处理逻辑 5. 任务入口函数k210_rx_task_entry的工作流程 6. 关键数据结构和外部依赖的说明 7. 可能的工作机制和设计优点 需要确保每个部分都简明扼要,逐步引导用户理解代码的实现。</think>以下是对`packet_portting.c`文件的逐层解析: --- ### 一、代码整体功能 该文件实现了基于**串口协议的数据接收与解析框架**,核心功能包括: - 使用DMA双缓冲接收串口数据,降低CPU负载 - 通过环形缓冲区(FIFO)实现数据缓 - 结合RTOS信号量实现任务同步 - 基于状态机的协议解析机制 --- ### 二、关键数据结构 ```c struct k210_PacketController { uint8_t state; // 协议解析状态机状态 uint16_t data_index; // 当前数据包索引 uint8_t* rx_dma_buffers[2]; // DMA双缓冲指针 uint16_t rx_dma_buffer_size; // 单个DMA缓冲大小 uint8_t rx_dma_buffer_index; // 当前使用的DMA缓冲索引 lwrb_t* rx_fifo; // 环形缓冲区控制结构 uint8_t* rx_fifo_buffer; // FIFO数据储区 }; ``` --- ### 三、核心函数解析 #### 1. 初始化函数 `k210_packet_init()` ```c void k210_packet_init(void) ``` - **作用**:初始化协议控制器硬件无关部分 - **关键操作**: - 清零控制器结构体 - 分配DMA双缓冲内(静态数组) - 动态分配FIFO缓冲区(使用`LWMEM_CCM_MALLOC`) - 初始化环形缓冲区(`lwrb_init`) #### 2. 接收启动函数 `k210_start_recv()` ```c void k210_start_recv(void) ``` - **流程**: 1. 注册DMA接收完成回调 2. 启动首次DMA接收(`HAL_UARTEx_ReceiveToIdle_DMA`) - **技术亮点**: - 使用`ReceiveToIdle`模式:在DMA满或检测到总线空闲时触发中断 #### 3. DMA回调函数 `k210_dma_receive_event_callback()` ```c void k210_dma_receive_event_callback(UART_HandleTypeDef *huart, uint16_t length) ``` - **触发时机**:DMA传输完成或检测到总线空闲 - **关键操作**: 1. 切换DMA缓冲索引(`rx_dma_buffer_index ^= 1`) 2. 立即启动新DMA接收 3. 将已接收数据写入FIFO(`lwrb_write`) 4. 释放信号量通知处理任务(`osSemaphoreRelease`) #### 4. 接收任务入口 `k210_rx_task_entry()` ```c void k210_rx_task_entry(void *argument) ``` - **工作流程**: 1. 初始化信号量状态 2. 注册协议处理回调 3. 进入主循环: - 等待信号量(`osSemaphoreAcquire`) - 调用协议解析函数(`k210_packet_recv`) --- ### 四、关键技术点 #### 1. 双缓冲DMA机制 - 使用`rx_dma_buffers[2]`实现乒乓缓冲 - 当前缓冲接收数据时,另一缓冲可用于数据处理 - 切换公式:$index_{new} = index_{old} \oplus 1$(异或操作) #### 2. 环形缓冲区管理 - 使用轻量级`lwrb`库实现FIFO - 缓冲区大小:$2048B$(通过`K210_RX_FIFO_BUFFER_SIZE`定义) - 写入操作线程安全(在中断上下文中操作) #### 3. 任务同步机制 - 信号量`k210_rx_not_emptyHandle`实现生产-消费模型 - 中断上下文释放信号量 → 任务上下文获取信号量 --- ### 五、数据流向 ``` 硬件层:UART → DMA双缓冲 ↓ 中断层:数据拷贝至FIFO → 触发信号量 ↓ 任务层:从FIFO取出数据 → 协议解析 ``` --- ### 六、设计优势分析 1. **低延迟**:DMA传输避免CPU参与数据搬运 2. **高可靠性**:双缓冲设计防止数据覆盖 3. **可扩展性**:通过状态机实现协议解析框架 4. **资源优化**:动态内分配仅用于长期在的缓冲 --- ### 七、潜在优化方向 1. 增加FIFO溢出检测机制 2. 实现动态DMA缓冲大小调整 3. 添加错误处理(CRC校验等) 4. 支持优先级继承的信号量 如需对特定实现细节展开说明,可提出进一步分析需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值