LoRA训练助手实战教程:3步生成SD/FLUX专用英文训练标签
å‰�ä¸¤ç¯‡æˆ‘ä»¬åˆ†åˆ«è§£å†³äº†â€œå†…å˜æ€�么分â€�和“模å�—æ€�么管â€�的问题。今天这一篇我们è¦�解决嵌入å¼�å¼€å�‘䏿œ€ç¡¬æ ¸ã€�也最考验功底的性能问题——数æ�®å��å��é‡�ã€‚å½“ä½ çš„æ³¢ç‰¹ç�‡é£™å�‡åˆ° 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。这需è¦�结å�ˆç¬¬ä¸€ç¯‡çš„å¯¹è±¡æ± æŠ€æœ¯æ�¥å®�ç�°é«˜çº§çš„å†…å˜æµ�转。
免费观看18++网站-免费观看18++网站应用