stm32f103zet6移植标准库的sdio驱动
sdio移植
st官网给的标准库有给一个用于st出的评估板的sdio外设实现,但一是文件结构有点复杂,二是相比于国内正点原子和野火的板子也有点不同,因此还是需要移植下才能使用。当然也可以直接使用正点原子或野火提供的实例,但为了熟悉下sdio的一些知识,本人还是决定从st标准库移植一次。下面就是移植的过程。
硬件差异
st官方提供的10E评估板和本人使用的野火stm32f103zet6开发板可以兼容。但在TF卡插入检测有所不同。st官方的stm3210e评估板使用PF11用于卡插入检测,而stm32f103zet6开发板没有提供此检测引脚功能。
移植内容
- 复制
STM32F10x_StdPeriph_Lib_V3.5.0/Utilities/STM32_EVAL/Common/stm32_eval_sdio_sd.c和STM32F10x_StdPeriph_Lib_V3.5.0/Utilities/STM32_EVAL/Common/stm32_eval_sdio_sd.h到自己的项目中 - 自己项目中新建一个
sdio_gpio.c和sdio_gpio.h。复制STM32F10x_StdPeriph_Lib_V3.5.0/Utilities/STM32_EVAL/STM3210E_EVAL/stm3210e_eval.c中的几个函数实现到sdio_gpio.c文件中。这里就列出sdio_gpio.h头文件,具体函数就自己复制了。
#ifndef __SDIO_GPIO_H
#define __SDIO_GPIO_H#include "stm32f10x_conf.h"// 这里的3个宏是驱动中会使用到的,都是官方实例中复制出来的
#define SDIO_FIFO_ADDRESS ((uint32_t)0x40018080)
/*** @brief SDIO Intialization Frequency (400KHz max)*/
#define SDIO_INIT_CLK_DIV ((uint8_t)0xB2)
/*** @brief SDIO Data Transfer Frequency (25MHz max)*/
#define SDIO_TRANSFER_CLK_DIV ((uint8_t)0x00)// 头文件中只是这几个函数的声明,函数的具体内容要复制到sd_gpio.c文件中
void SD_LowLevel_DeInit(void);
void SD_LowLevel_Init(void);
void SD_LowLevel_DMA_TxConfig(uint32_t *BufferSRC, uint32_t BufferSize);
void SD_LowLevel_DMA_RxConfig(uint32_t *BufferDST, uint32_t BufferSize);
uint32_t SD_DMAEndOfTransferStatus(void);
// 这里中断初始化改了一个名字,防止和别的外设中断初始化重名
void sdio_NVIC_Configuration(void)#endif
- 复制好函数体后,删除
sdio_gpio.c文件中出现"DETECT"的地方,原因就是因为官方评估板中有DETECT引脚,而本人板子实际没有DETECT引脚 - 更改一些
stm32_eval_sdio_sd.c文件中的内容
static SD_Error CmdResp2Error(void);
//这里注释了,因为在sd_gpio.c中定义了,不再是私有函数
//static uint32_t SD_DMAEndOfTransferStatus(void);
static SD_Error CmdResp6Error(uint8_t cmd, uint16_t *prca);// SD_Init()函数中加入中断初始化
SD_Error SD_Init(void)
{SD_Error errorstatus = SD_OK;/* SDIO Peripheral gpio 初始化 */SD_LowLevel_Init();SDIO_DeInit();// 中断初始化sdio_NVIC_Configuration();errorstatus = SD_PowerON();...
}// SD_GetState()删除detect内容
/*** @brief Returns the current card's state.* @brief 因为采用data3信号线作为插入检测,因此此处不能使用SD_Detect()函数* @param None* @retval SDCardState: SD Card Error or SD Card Current State.*/
SDCardState SD_GetState(void)
{uint32_t resp1 = 0;/*if(SD_Detect()== SD_PRESENT)*//*{*/if (SD_SendStatus(&resp1) != SD_OK){return SD_CARD_ERROR;}else{return (SDCardState)((resp1 >> 9) & 0x0F);}/*}*//*else*//*{*//*return SD_CARD_ERROR;*//*}*/
}// SD_Detect()函数全部注释了
/*** @brief Detect if SD card is correctly plugged in the memory slot.* @brief 因为没有单独的插入检测,因此不使用此函数* @param None* @retval Return if SD is detected or not*/
/*uint8_t SD_Detect(void)*/
/*{*//*__IO uint8_t status = SD_PRESENT;*//*[>!< Check GPIO to detect SD <]*//*if (GPIO_ReadInputDataBit(SD_DETECT_GPIO_PORT, SD_DETECT_PIN) != Bit_RESET)*//*{*//*status = SD_NOT_PRESENT;*//*}*//*return status;*/
/*}*/// SD_WriteMultiBlocks()中加入一个命令,书中说加入有用,具体不清楚
SD_Error SD_WriteMultiBlocks(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize, uint32_t NumberOfBlocks)
{SD_Error errorstatus = SD_OK;__IO uint32_t count = 0;TransferError = SD_OK;TransferEnd = 0;StopCondition = 1;SDIO->DCTRL = 0x0;if (CardType == SDIO_HIGH_CAPACITY_SD_CARD){BlockSize = 512;WriteAddr /= 512;}/*!< 根据正点原子代码加入,说明是为了防止DMA检测卡死 */SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);if (errorstatus != SD_OK){return(errorstatus);}...
}
- 还有一个sdio的中断函数还要忘记了,放在
stm32f10x_it.c或main.c都一样的。
/** 函数名:SDIO_IRQHandler* 描述 :在SDIO_ITConfig()这个函数开启了sdio中断 ,数据传输结束时产生中断* 输入 :无* 输出 :无*/
void SDIO_IRQHandler(void)
{/* Process All SDIO Interrupt Sources */SD_ProcessIRQSrc();
}
- 改好后项目中加入
sdio_gpio.h和stm32_eval_sdio_sd.h头文件。就可以试着编译下了
加入测试代码
因为本人之前的项目中使用的freertos,且已经调通uart0串口。st官方实例是使用几个LED灯指示测试结果,本人开发板上没有这么多的灯,就直接使用串口的printf显示了,一样的方便。下面就是main.c文件。
#include "stm32f10x_conf.h"//extern __IO uint16_t ADCConvertedValue;
extern __IO uint16_t ADC_DualConvertedValueTab[4];
extern QueueHandle_t command_buf;typedef enum {FAILED = 0, PASSED = !FAILED} TestStatus;
#define BLOCK_SIZE 512 /* Block Size in Bytes */#define NUMBER_OF_BLOCKS 32 /* For Multi Blocks operation (Read/Write) */
#define MULTI_BUFFER_SIZE (BLOCK_SIZE * NUMBER_OF_BLOCKS)#define SD_OPERATION_ERASE 0
#define SD_OPERATION_BLOCK 1
#define SD_OPERATION_MULTI_BLOCK 2
#define SD_OPERATION_END 3uint8_t Buffer_Block_Tx[BLOCK_SIZE], Buffer_Block_Rx[BLOCK_SIZE];
uint8_t Buffer_MultiBlock_Tx[MULTI_BUFFER_SIZE], Buffer_MultiBlock_Rx[MULTI_BUFFER_SIZE];void SD_EraseTest(void);
void SD_SingleBlockTest(void);
void SD_MultiBlockTest(void);
void Fill_Buffer(uint8_t *pBuffer, uint32_t BufferLength, uint32_t Offset);
TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint32_t BufferLength);
TestStatus eBuffercmp(uint8_t* pBuffer, uint32_t BufferLength);
SD_Error Status = SD_OK;
volatile TestStatus EraseStatus = FAILED, TransferStatus1 = FAILED, TransferStatus2 = FAILED;
__IO uint32_t SDCardOperation = SD_OPERATION_ERASE;void sdio_test_task(void *pvParameter)
{if((Status = SD_Init()) != SD_OK)printf("sdio init error");while(Status == SD_OK && SDCardOperation != SD_OPERATION_END){switch(SDCardOperation){case SD_OPERATION_ERASE:SD_EraseTest();SDCardOperation = SD_OPERATION_BLOCK;break;case SD_OPERATION_BLOCK:SD_SingleBlockTest();SDCardOperation = SD_OPERATION_MULTI_BLOCK;break;case SD_OPERATION_MULTI_BLOCK:SD_MultiBlockTest();SDCardOperation = SD_OPERATION_END;break;}}while(1){}
}void dac_task(void *pvParameter){static unsigned short num = 0xffff;for(;;){DAC_SetChannel1Data(DAC_Align_12b_L, num);DAC_SoftwareTriggerCmd(DAC_Channel_1, ENABLE);num -= 0x2;if(num < 0xff)num = 0xffff;vTaskDelay(2);}
}void system_init(void *pvParameter){SystemInit();dac_init();serial_init();//single_adc_init();muiltichannel_adc_init();command_buf = xQueueCreate(100, sizeof(struct command_t));vTaskDelete(NULL);
}void print_test_task(void *pvParameter){for(;;){printf("This just test for uart\r\n");vTaskDelay(500);}
}//void adc_task(void *pvParameter){
// // 软件开启ADC1转换
// ADC_SoftwareStartConvCmd(ADC1, ENABLE);
// for(;;){
// printf("Current ADC voltage is %f\r\n", ADCConvertedValue * 3.3/4096);
// vTaskDelay(5);
// }
//}void muiltichannel_adc_task(void *pvParameter){muiltichannel_adc_enable();for(;;){printf("PA1 voltage is %.2f\r\n", ADC_DualConvertedValueTab[0] * 3.3 / 4096);printf("PA2 voltage is %.2f\r\n", ADC_DualConvertedValueTab[1] * 3.3 / 4096);printf("PA3 voltage is %.2f\r\n", ADC_DualConvertedValueTab[2] * 3.3 / 4096);printf("PA5 voltage is %.2f\r\n", ADC_DualConvertedValueTab[3] * 3.3 / 4096);vTaskDelay(2);}
}int main(void)
{xTaskCreate(system_init, "system init task", 500, NULL, 2, NULL);//xTaskCreate(print_test_task, "uart print test", 200, NULL, 1, NULL);/*xTaskCreate(dac_task, "dac task for led breath", 1000, NULL, 1, NULL);*///xTaskCreate(adc_task, "adc test task", 1000, NULL, 1, NULL);/*xTaskCreate(muiltichannel_adc_task, "mNuiltichannel adc test task", 1000, NULL, 1, NULL);*/xTaskCreate(sdio_test_task, "sdio test", 200, NULL, 1, NULL);vTaskStartScheduler();
}#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{while (1){printf("Assert error in %s, line %d\r\n",file, line);}
}
#endifvoid vApplicationStackOverflowHook( TaskHandle_t xTask,char * pcTaskName ){while (1){printf("Malloc error for %s\r\n",pcTaskName);}
}void SD_EraseTest(void)
{if (Status == SD_OK){/* Erase NumberOfBlocks Blocks of WRITE_BL_LEN(512 Bytes) */Status = SD_Erase(0x00, (BLOCK_SIZE * NUMBER_OF_BLOCKS));}if (Status == SD_OK){Status = SD_ReadMultiBlocks(Buffer_MultiBlock_Rx, 0x00, BLOCK_SIZE, NUMBER_OF_BLOCKS);/* Check if the Transfer is finished */Status = SD_WaitReadOperation();/* Wait until end of DMA transfer */while(SD_GetStatus() != SD_TRANSFER_OK);}/* Check the correctness of erased blocks */if (Status == SD_OK){EraseStatus = eBuffercmp(Buffer_MultiBlock_Rx, MULTI_BUFFER_SIZE);}if(EraseStatus == PASSED){printf("sdio erase test passed\r\n");}else{printf("sdio erase test failed\r\n");}
}void SD_SingleBlockTest(void)
{/* Fill the buffer to send */Fill_Buffer(Buffer_Block_Tx, BLOCK_SIZE, 0x320F);if (Status == SD_OK){/* Write block of 512 bytes on address 0 */Status = SD_WriteBlock(Buffer_Block_Tx, 0x00, BLOCK_SIZE);/* Check if the Transfer is finished */Status = SD_WaitWriteOperation();while(SD_GetStatus() != SD_TRANSFER_OK);}if (Status == SD_OK){/* Read block of 512 bytes from address 0 */Status = SD_ReadBlock(Buffer_Block_Rx, 0x00, BLOCK_SIZE);/* Check if the Transfer is finished */Status = SD_WaitReadOperation();while(SD_GetStatus() != SD_TRANSFER_OK);}/* Check the correctness of written data */if (Status == SD_OK){TransferStatus1 = Buffercmp(Buffer_Block_Tx, Buffer_Block_Rx, BLOCK_SIZE);}if(TransferStatus1 == PASSED){printf("sdio single block test passed\r\n");}else{printf("sdio single block test failed\r\n");}
}void SD_MultiBlockTest(void)
{/*--------------- Multiple Block Read/Write ---------------------*//* Fill the buffer to send */Fill_Buffer(Buffer_MultiBlock_Tx, MULTI_BUFFER_SIZE, 0x0);if (Status == SD_OK){/* Write multiple block of many bytes on address 0 */Status = SD_WriteMultiBlocks(Buffer_MultiBlock_Tx, 0x00, BLOCK_SIZE, NUMBER_OF_BLOCKS);/* Check if the Transfer is finished */Status = SD_WaitWriteOperation();while(SD_GetStatus() != SD_TRANSFER_OK);}if (Status == SD_OK){/* Read block of many bytes from address 0 */Status = SD_ReadMultiBlocks(Buffer_MultiBlock_Rx, 0x00, BLOCK_SIZE, NUMBER_OF_BLOCKS);/* Check if the Transfer is finished */Status = SD_WaitReadOperation();while(SD_GetStatus() != SD_TRANSFER_OK);}/* Check the correctness of written data */if (Status == SD_OK){TransferStatus2 = Buffercmp(Buffer_MultiBlock_Tx, Buffer_MultiBlock_Rx, MULTI_BUFFER_SIZE);}if(TransferStatus2 == PASSED){printf("sdio muilti block test passed\r\n");}else{printf("sdio muilti block test error\r\n");}
}TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint32_t BufferLength)
{while (BufferLength--){if (*pBuffer1 != *pBuffer2){return FAILED;}pBuffer1++;pBuffer2++;}return PASSED;
}void Fill_Buffer(uint8_t *pBuffer, uint32_t BufferLength, uint32_t Offset)
{uint16_t index = 0;/* Put in global buffer same values */for (index = 0; index < BufferLength; index++){pBuffer[index] = index + Offset;}
}TestStatus eBuffercmp(uint8_t* pBuffer, uint32_t BufferLength)
{while (BufferLength--){/* In some SD Cards the erased state is 0xFF, in others it's 0x00 */if ((*pBuffer != 0xFF) && (*pBuffer != 0x00)){return FAILED;}pBuffer++;}return PASSED;
}
以上的大部分内容都是复制的STM32F10x_StdPeriph_Lib_V3.5.0/Project/STM32F10x_StdPeriph_Examples/SDIO/uSDCard/main.c的内容。只是在此基础上加入了freertos的格式,将测试最后通过和出错的提示方式全部使用串口打印出来。
下面是串口显示的打印结果:
sdio erase test passed
sdio single block test passed
sdio muilti block test passed
至此sdio基本驱动移植成功,可以移植fatfs了。
相关文章:
stm32f103zet6移植标准库的sdio驱动
sdio移植 st官网给的标准库有给一个用于st出的评估板的sdio外设实现,但一是文件结构有点复杂,二是相比于国内正点原子和野火的板子也有点不同,因此还是需要移植下才能使用。当然也可以直接使用正点原子或野火提供的实例,但为了熟…...
为什么vector容器的begin()既可以被iterator 也可以被const_iterator指向?
答:vector容器中的begin()是函数接口,它作为函数,被重载了。 typedef T* iterator; typedef const T* const_iterator; iterator begin();//括号中有隐含形参*this; const_iterator begin() const;//形参为…...
uniapp里textarea多行文本输入限制数量
uniapp里textarea多行文本域实现输入计数 <template><view class"inputs"><textarea class"text1" maxlength50 placeholder请输入... input"sumfontnum"></textarea><text class"text2">{{fontNum}}/…...
真香:Alibaba开源GitHub星标100K微服务架构全彩进阶手册
前言: 微服务架构作为一种高效灵活的应用架构,正在成为企业级应用开发的主流选择。在众多的微服务架构指南中,阿里巴巴开源的GitHub微服务架构全彩进阶手册备受瞩目,其100star更是证明了其在开发者社区中的重要地位。 这本手册汇…...
Mysql--事务
事务 开始之前,让我们先想一个场景,有的时候,为了完成某个工作,需要完成多种sql操作 比如转账 再比如下单 第一步 我的账户余额减少 第二步 商品的库存要减少 第三步 订单表中要新增一项 事务的本质,就是为了把多个操…...
【算法题】小红书2023秋招提前批算法真题解析
文章目录 题目来源T1:5900: 【DP】小红书2023秋招提前批-连续子数组最大和5801: 【二分查找】小红书2023秋招提前批-精华帖子解法1——排序滑动窗口解法2——前缀和 二分查找 5000: 【模拟】小红书2023秋招提前批-小红的数组构造解法——数学 5300: 【哈希表】小红…...
序列到序列学习(seq2seq)
permute(1,0,2),将batch_size 放在中间state 最后一个时刻,每个层的输出...
基于Java+SpringBoot+Vue摄影分享网站的设计与实现 前后端分离【Java毕业设计·文档报告·代码讲解·安装调试】
🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…...
接口测试系列 —— POSTMAN的简单使用
postman的基本使用 概述 我相信对于postman的介绍,网上一搜肯定很多很多。下面我就不打算跟大家普及postman了。只看应该怎么用postman进行接口测试。好了,下面咱们直接进入正文吧。 环境 postman之前是作为chrome插件形式存在的。后面变成了独立的应…...
一个帮各位填秋招表格省一点事的浏览器插件
最近应该很多和我一样的双非鼠鼠在秋招等面试,而且处于海投阶段,为了不忘记投了哪些公司,可以用这样一个表格来记录: 其中有些字段,比如状态、投递时间、查看进度的网址其实可以不手动输入,所以搞个插件来…...
react16之前diff算法的理解和总结
此篇文章所讨论的是 React 16 以前的 Diff 算法。而 React 16 启用了全新的架构 Fiber,相应的 Diff 算法也有所改变,本片不详细讨论Fiber。 fiber架构是为了支持react进行可中断渲染,降低卡顿,提升流畅度。 react16之前的版本&…...
JavaEE初阶(1)(冯诺依曼体系、CPU、CPU基本原理、如何衡量CPU的好坏?指令、操作系统、操作系统“内核”)
目录 冯诺依曼体系(Von Neumann Architecture) CPU CPU基本原理: 如何衡量CPU的好坏? 1、主频(时钟速度): 2、核心数: 指令 操作系统 操作系统“内核” 冯诺依曼体系&#x…...
记录在yapi上传接口的问题
sorry ,upload api error cause:请求参数 data.path 不应少于 1 个字符 自己在写的代码中使用到了DeleteMapping DeleteMapping("/deleteCart/{skuId}")public Result deleteCart(PathVariable Long skuId,HttpServletRequest request){报上面的错误,原因…...
DevOps管理软件生命周期
整体的软件开发流程 PLAN:开发团队根据客户的目标制定开发计划 CODE:根据PLAN开始编码过程,需要将不同版本的代码存储在一个库中。GIT,SVN BUILD:编码完成后,需要将代码构建并且运行。MAVEN TEST:成功构建…...
快速解决 adb server version doesn‘t match this client
这个问题是由于电脑上安装了多个版本的adb工具,客户端和服务端的版本不一致,无法正常通信导致。最快的解决方法就是将Android SDK中adb复制到系统目录下。 操作步骤如下: 1. 查看adb版本和路径 执行adb version,如下࿰…...
【更新至2022年】2000-2022年全国31省市以2000年为基期的实际GDP、名义GDP、GDP平减指数数据(含原始数据+计算过程+计算结果)
2000-2022年31省市名义GDP 实际GDP GDP平减指数 1、时间:2000-2022 2、范围:31省市 3、来源:GJ统计J和统计NJ 4、指标:名义GDP、地区生产总值指数(上年100)、实际GDP(以2000年为基期&#x…...
【LeetCode】剑指 Offer <二刷>(5)
目录 题目:剑指 Offer 10- II. 青蛙跳台阶问题 - 力扣(LeetCode) 题目的接口: 解题思路: 代码: 过啦!!! 题目:剑指 Offer 11. 旋转数组的最小数字 - 力…...
rtsp 拉流 gb28181 收流 经AI 算法 再生成 rtsp server (一)
1、 rtsp 工具 1 vlc 必备工具 2 wireshark 必备工具 3 自己制作的工具 player 使用tcp 拉流,不自己写的话,使用ffmpeg 去写一个播放器就行 4 live555 编译好live555, 将live555的参数修改以下,主要是缓存大小 文章使用c 来写一…...
Jmeter系列-环境部署、详细介绍、安装目录介绍(1)
环境部署 官网下载Jmeter http://jmeter.apache.org/下载最新版本的 JMeter,解压文件到任意目录 安装JDK,配置Java环境 1、下载(注意选择操作系统对应的位数32/64) 官网 :http://www.oracle.com 2、安装࿰…...
更换 yum 阿里源 - 手把手教你怎么配置,在也不需要求别人了 - 看懂一个就相当于看懂了其他的linux系统
更换阿里源 我的是centos8 当然 centos7 也可以换 后面有更详细的怎么配 ,再也不用求别人怎么弄了 最直接的方式 直接复制 执行 centos7 curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo或者 wget -O /etc/yum.repos.…...
STM32CubeIDE实战:HAL库串口中断接收的5个常见坑点及解决方案
STM32CubeIDE实战:HAL库串口中断接收的5个常见坑点及解决方案 在工业传感器数据采集、设备间通信等场景中,稳定可靠的串口通信往往是嵌入式开发的关键环节。许多开发者在使用STM32CubeIDE配合HAL库实现串口中断接收时,虽然能够快速搭建基础功…...
R语言实战:用sf和ggplot2绘制带比例尺和指北针的专业地图(附完整代码)
R语言地理信息可视化实战:从数据到专业地图的完整指南 地理信息数据可视化是科研和商业分析中不可或缺的一环。无论是环境监测、城市规划还是流行病学研究,将空间数据转化为直观的地图都能极大提升数据洞察力。本文将手把手教你使用R语言中的sf和ggplot2…...
物联网项目实战:ESP32S3 解析 AS608 指纹特征数据包(二)
1. 数据包结构深度解析 第一次拿到AS608指纹模块的原始数据包时,我盯着那一串十六进制数看了足足半小时。就像拆解一个俄罗斯套娃,需要层层剥离才能找到核心的指纹特征数据。实测发现,完整的数据包包含三个关键部分: 包头标识&…...
ELK+Metricbeat搭建服务器监控看板:CPU/内存/磁盘全搞定
ELKMetricbeat实战:打造企业级服务器监控看板 当服务器集群规模超过50台时,凌晨三点被电话叫醒处理性能问题的运维人员,最需要的不是咖啡,而是一套能实时呈现CPU、内存、磁盘等关键指标的智能监控系统。本文将手把手带您用ELK Sta…...
Get Shit Done:基于上下文工程的AI开发框架解决Claude Code上下文衰退难题
Get Shit Done:基于上下文工程的AI开发框架解决Claude Code上下文衰退难题 【免费下载链接】get-shit-done A light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code by TCHES. 项目地址: https://…...
从‘分式规划’到‘加减法’:二次变换如何成为通信优化工程师的‘瑞士军刀’
从‘分式规划’到‘加减法’:二次变换如何成为通信优化工程师的‘瑞士军刀’ 通信系统优化中,工程师常遇到一类令人头疼的问题:目标函数是分式形式,且分子分母都包含待优化变量。这类问题在能效优化、频谱效率提升等场景中尤为常见…...
用ESP32-S3给OV2640摄像头上‘网课’:手把手实现低延迟MJPEG监控系统
基于ESP32-S3与OV2640构建低延迟MJPEG监控系统的工程实践 在物联网和边缘计算领域,实时视频监控系统的需求日益增长。本文将深入探讨如何利用ESP32-S3微控制器和OV2640摄像头模组构建一个完整的低延迟MJPEG监控系统,从硬件连接到软件优化,全…...
文脉定序应用场景:企业知识库‘搜得到更排得准’的语义校准落地方案
文脉定序应用场景:企业知识库搜得到更排得准的语义校准落地方案 1. 企业知识库检索的痛点与挑战 在企业日常运营中,知识库扮演着重要角色。无论是产品文档、技术资料、客户案例还是内部流程,都需要一个高效的知识检索系统。然而,…...
深入剖析torchvision Faster-RCNN ResNet-50 FPN中的RPN机制与实现细节
1. RPN模块在Faster-RCNN中的核心作用 当你第一次接触目标检测时,可能会被各种专业术语搞得晕头转向。但别担心,RPN(Region Proposal Network)其实就像是一个"智能扫描仪",它的任务就是在图像中快速找出可能…...
GLM-4V-9B真实案例展示:从上传JPG到输出结构化文本的端到端演示
GLM-4V-9B真实案例展示:从上传JPG到输出结构化文本的端到端演示 1. 项目背景与核心价值 GLM-4V-9B作为多模态大模型的优秀代表,能够同时理解图像和文本信息,实现真正的视觉-语言交互。但在实际部署中,很多开发者会遇到环境兼容性…...
