硕士文献综述 “破卷” 指南:PaperZZ 如何用 AI 终结文献梳理的 “体力内卷”

核心内容摘要

MusePublic圣光艺苑:新手必学的AI绘画技巧
Java SpringBoot+Vue3+MyBatis springcloud微服务车联网位置信息管理软件系统源码|前后端分离+MySQL数据库

STM32万年历系统:模块化架构与串口粘包解决方案

å‰�两篇我们分别解决了“内存æ€�么分â€�和“模å�—æ€�么管â€�的问题。今天这一篇我们è¦�解决嵌入å¼�å¼€å�‘ä¸­æœ€ç¡¬æ ¸ã€�也最考验功底的性能问题——数æ�®å��å��é‡�ã€‚å½“ä½ çš„æ³¢ç‰¹ç�‡é£™å�‡åˆ° 2Mbps或者 ADC é‡‡æ ·ç�‡è¾¾åˆ° 1Msps æ—¶å�•纯é� 中断ISR一个字节一个字节地收CPU 早就累å��血了。这时候 DMAç›´æ�¥å­˜å‚¨å™¨è®¿é—®æ˜¯æ•‘星但如æ�œ DMA 用ä¸�好数æ�®è¦†ç›–å’Œæ’•è£‚é—®é¢˜ä¼šè®©ä½ æ€€ç–‘äººç”Ÿã€‚æ˜¯æ—¶å€™ç¥­å‡ºå�Œç¼“冲模å¼�了。专æ �导读在高速数æ�®é‡‡é›†æˆ–显示系统中CPU 处ç�†æ•°æ�®çš„速度往往跟ä¸�上硬件产生数æ�®çš„速度。如æ�œå�ªæœ‰ä¸€ä¸ªç¼“冲区CPU 在读硬件在写è¦�么读到è„�æ•°æ�®è¦�么必须关硬件等待。å�Œç¼“冲Ping-Pong Buffer通过“空间æ�¢æ—¶é—´â€�å®�ç�°äº†ç¡¬ä»¶ä¼ 输ä¸� CPU 处ç�†çš„完ç¾�并行。

场景还å�Ÿ (The Pain)å�‡è®¾ä½ 正在å�šä¸€ä¸ªé«˜ä¿�真录音笔ADC 以

4

1kHz é‡‡æ ·é€šè¿‡ DMA 往内存里æ�¬è¿�æ•°æ�®ã€‚æ¯�采集 1024 ä¸ªç‚¹ä½ éœ€è¦�把数æ�®å†™å…¥ SD å�¡ã€‚è�œé¸Ÿçš„写法å�•缓冲的ç«�æ€�冒险#define BUF_SIZE 1024volatile uint16_t g_adc_buffer[BUF_SIZE];// DMA ä¼ è¾“å®Œæˆ�中断void DMA_TC_Handler(void) {// 痛点在把数æ�®å†™å…¥ SD å�¡æœŸé—´å¿…é¡»å�œæ­¢ ADC é‡‡æ ·// å�¦åˆ™ DMA 会ä»�头开始覆盖数æ�®å¯¼è‡´ CPU 写å�¡è¯»åˆ°çš„å‰�å�Šæ®µæ˜¯æ—§æ•°æ�®å��å�Šæ®µæ˜¯æ–°æ•°æ�®æ•°æ�®æ’•裂。HAL_ADC_Stop_DMA(); //

å�œç¡¬ä»¶å¯¼è‡´é‡‡æ ·ä¸¢å¤±å½•音断续Write_SD_Card(g_adc_buffer, BUF_SIZE); //

耗时�作比如 10msHAL_ADC_Start_DMA(); //

é‡�新开硬件}æ�¶æ�„师的审视这ç§�**“å�œ-读-å¼€â€�**çš„é€»è¾‘åœ¨é«˜é¢‘é‡‡æ ·ä¸‹æ˜¯æ— æ³•æ�¥å�—的。数æ�®ä¸¢å¤±å†™ SD å�¡çš„é‚£ 10ms 里麦克é£�采集的声音全丢了。CPU 利用ç�‡ä½�CPU 写å�¡æ—¶ DMA é—²ç�€DMA æ�¬è¿�æ—¶ CPU é—²ç�€å¦‚æ�œæ²¡å…¶ä»–任务没有å®�ç�°æµ�水线并行。

模å¼�图解 (The Concept)å�Œç¼“冲模å¼�也å�« Ping-Pong Bufferå‡†å¤‡äº†ä¸¤ä¸ªä¸€æ ·å¤§çš„ç¼“å†²åŒºBuffer A (Ping) å’Œ Buffer B (Pong)。状æ€� 0DMA 正在疯狂填充Buffer ACPU 闲置或处ç�†å…¶ä»–业务。状æ€� 1Buffer A 填满了。DMA ç«‹å�³è‡ªåŠ¨åˆ‡æ�¢åˆ°Buffer Bç»§ç»­å¡«å……ç¡¬ä»¶æ— ç¼�切æ�¢ã€‚状æ€� 2此时 CPU 醒æ�¥å¤„ç�†åˆšåˆšå¡«æ»¡çš„Buffer A比如写å�¡ã€�DSP计算ä¸�æ­¤å�Œæ—¶DMA 正在默默填充 Buffer B。循ç�¯Buffer B 填满å��DMA 切å›� ACPU 处ç�† Bã€‚æ ¸å¿ƒä¼˜åŠ¿ç¡¬ä»¶DMA永远ä¸�需è¦�å�œCPU 永远在处ç�†â€œé�™æ€�â€�的数æ�®ã€‚

代ç �å®�战 (The Code)ç�°ä»£ MCU如 STM32çš„ DMA 通常自带“循ç�¯æ¨¡å¼�â€�和“å�Šä¼ 输/å…¨ä¼ è¾“ä¸­æ–­â€�这天然支æŒ�å�Œç¼“冲。但为了通用性我们写一个逻辑层的å°�装让它看起æ�¥æ›´åƒ�一个通用的设计模å¼�。

1 定义数æ�®ç»“æ�„#include stdint.h#include stdbool.h#define SAMPLE_COUNT 1024 // å�•个缓冲区大å°�#define BUFFER_TOTAL 2 // å�Œç¼“冲也å�¯ä»¥æ‰©å±•æˆ�三缓冲typedef struct {// 定义一个二维数组buffer[2][1024]uint16_t raw_data[BUFFER_TOTAL][SAMPLE_COUNT];// 当å‰� CPU 应该处ç�†å“ªä¸ª Buffer 的索引volatile uint8_t process_index;// æ ‡å¿—ä½�告诉主循ç�¯æœ‰æ•°æ�®å‡†å¤‡å¥½äº†volatile bool data_ready;} PingPongBuffer;static PingPongBuffer g_adc_pp_buf;

2 中断逻辑 (The Core Logic)这里利用 DMA 的两个中断事件Half Transfer (HT)表示å‰�一å�ŠBuffer 0填满了。Transfer Complete (TC)表示整个大数组填满了å�³ Buffer 1 也填满了此时 DMA 会自动循ç�¯å›�到开头。// 伪代ç �对应具体的硬件中断å›�è°ƒvoid DMA_IRQ_Handler(void) {uint32_t status DMA_GetStatus();//

å�Šä¼ 输中断 (Half Transfer) - Buffer 0 满了if (status DMA_FLAG_HT) {g_adc_pp_buf.process_index 0; // 告诉 CPU å�»å¤„ç�† Buffer 0g_adc_pp_buf.data_ready true;DMA_ClearFlag_HT();}//

ä¼ è¾“å®Œæˆ�中断 (Transfer Complete) - Buffer 1 满了if (status DMA_FLAG_TC) {g_adc_pp_buf.process_index 1; // 告诉 CPU å�»å¤„ç�† Buffer 1g_adc_pp_buf.data_ready true;DMA_ClearFlag_TC();}}

3 主循�处� (Consumer)// 模拟��的 DSP 处�或写��作void Process_Data(uint16_t* data, uint32_t len) {// 在这里写 SD �或者� FFT// 由�是�缓冲这里的�作�使耗时 5ms// ���� DMA 填满�一个 Buffer 的时间系统就是安全的。}void Main_Loop(void) {// �动 DMA长度设为 2 * SAMPLE_COUNT// 必须开� Circular Mode (循�模�)HAL_DMA_Start(..., (uint32_t)g_adc_pp_buf.raw_data, SAMPLE_COUNT *

;while (

{if (g_adc_pp_buf.data_ready) {//

关中断ä¿�æŠ¤æ ‡å¿—ä½�简å�•处ç�†// å®�际上 data_ready 最好用信å�·é‡� (Semaphore)g_adc_pp_buf.data_ready false;//

��当�应该处�的 Buffer 指针uint16_t* current_buf g_adc_pp_buf.raw_data[g_adc_pp_buf.process_index];//

处�数� (此时 DMA 正在写�一个 Buffer互�干扰)Process_Data(current_buf, SAMPLE_COUNT);}}}

内存ä¸�性能分æ�� (The Cost)空间开销RAM ç¿»å€�这是显而易è§�的代价。如æ�œæœ¬æ�¥éœ€è¦� 1KB 缓冲ç�°åœ¨éœ€è¦� 2KB。æ�ƒè¡¡åœ¨å­˜å‚¨å»‰ä»·çš„今天用 1KB RAM æ�¢å�– 100% 的数æ�®å®Œæ•´æ€§å’Œ CPU 并行度这笔买å�–æ��其划算。时间约æ�Ÿ (Time Constraints)å�Œç¼“冲ä¸�是万能的它有一个硬性物ç�†çº¦æ�ŸCPU 处ç�†ä¸€ä¸ª Buffer 的时间 DMA 填满一个 Buffer 的时间如æ�œ ADC é‡‡æ ·æ��快填满一个 Buffer å�ªè¦� 1ms而写 SD å�¡éœ€è¦� 10ms那么å�Œç¼“冲也会爆Overrun。解法这ç§�æƒ…å†µä¸‹ä½ éœ€è¦�çš„ä¸�是更多的缓冲而是å�‹ç¼©æ•°æ�®ã€�é™�ä½�é‡‡æ ·ç�‡æˆ–者æ�¢æ›´å¿«çš„ CPU/存储介质。

���延伸 (The Evolution)

1 ç�¯å½¢ç¼“冲区 (Ring Buffer / FIFO)很多åˆ�学者分ä¸�清å�Œç¼“冲和 Ring Buffer。Ring Buffer适å�ˆå­—节æµ� (Byte Stream)如串å�£ä¸�定长æ�¥æ”¶ã€‚通常是生产者ISR和消费者Task都在æ“�作å�Œä¸€ä¸ªå¤§æ•°ç»„的读写指针。Double Buffer适å�ˆå�—ä¼ è¾“ (Block Transfer)如 ADCã€�æ‘„åƒ�头图åƒ�ã€�USB æ•°æ�®åŒ…。结å�ˆä½“ä½ å�¯ä»¥ç”¨ DMA å¾€ Ring Buffer 里写但逻辑上ä¾�ç„¶å�¯ä»¥æŠŠ Ring Buffer 切割æˆ� n 个“片段â€�æ�¥ç®¡ç�†è¿™å…¶å®�就是多缓冲。

2 三缓冲 (Triple Buffering)在图形显示GUI领域é��常常è§�。Buffer A: 显示å±�正在显示这一帧。Buffer B: GPU/DMA 正在渲染/æ�¬è¿�下一帧。Buffer C: CPU 正在计算逻辑准备å†�ä¸‹ä¸€å¸§ã€‚è¿™æ ·å�¯ä»¥å½»åº•消除画é�¢æ’•裂å®�ç�°ä¸�般顺滑的 UI 体验。

3 é›¶æ‹·è´� (Zero-Copy) 驱动链在很多高级å��è®®æ ˆå¦‚ LwIP中å�Œç¼“冲的指针会直æ�¥ä¼ 递给下一层而ä¸�是memcpy。 比如DMA 收到了 Ping Buffer - ä¼ é€’æŒ‡é’ˆç»™ç½‘ç»œå±‚ - 网络层处ç�†å®Œ - 归还指针给 DMA。这需è¦�结å�ˆç¬¬ä¸€ç¯‡çš„å¯¹è±¡æ± æŠ€æœ¯æ�¥å®�ç�°é«˜çº§çš„内存æµ�转。

看片-看片应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123