STM32HAL庫學習筆記

目錄

定時器

一些小細節

輸入捕獲計算信號頻率

輸入捕獲計算占空比與頻率

使用定時器不改變占空比的同時改變頻率的方法

串口

重定向原理

重定向代碼

怎么從串口接收到的字符串數據中解析出float型的數據

strchr

sscanf

memset

第一種實現方法

RTC實時時鐘

LCD顯示

%s占位符與%c占位符的區別

I2C讀寫操作


定時器

一些小細節

其中HAL_TIM_Base_Start和HAL_TIM_Base_Start_IT這兩個函數是不能夠同時調用的,

  • ??HAL_TIM_Base_Start()??:僅啟動定時器,不涉及中斷
  • ??HAL_TIM_Base_Start_IT()??:啟動定時器并開啟更新中斷
  • ??不能同時調用??:二者會沖突操作硬件寄存器和 HAL 庫狀態機
  • ??正確做法??:根據需求選擇其中一個函數,或通過?HAL_TIM_ENABLE_IT()?動態管理中斷

輸入捕獲計算信號頻率

僅僅用于信號頻率的檢測就只需要使用定時器的上升沿觸發就可以了 , 捕獲兩個高電平的之間時間 ,用定時器時鐘比上時間差就能得到頻率 , 需要注意的細節在于+1的操作 , 因為計數都是從零開始的

下面是定時器17的輸入捕獲配置

?

//主函數里面打開定時器17的輸入捕獲中斷
HAL_TIM_IC_Start_IT(&htim17,TIM_CHANNEL_1);//編寫中斷回調函數
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM17){//測量頻率就是定時器的時鐘比上上升沿計數值capture = HAL_TIM_ReadCapturedValue(&htim17 ,TIM_CHANNEL_1);TIM17->CNT = 0;fre_out = main_clock/(capture*((TIM17->PSC)+1));}
}

輸入捕獲計算占空比與頻率

使用定時器3 , combined mode的PWM input on CH1實現

通道2配置為直接模式 , 通道2配置為間接模式(硬件自動配置) , 通道1和通道2分別捕獲上升沿和下降沿 , 設置濾波系數都為3.

配置定時器預分頻系數為80(從0開始計數)?, 主頻80M?, 所以定時器時鐘為1M , 頻率的計算就是定時器時鐘/上升沿到上升沿的時間

//主函數中打開定時器三的輸入捕獲中斷,兩個通道都需要打開
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2);//編寫中斷函數
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM3){//如果是定時器3的中斷period = TIM3->CCR1+1;//硬件自動捕獲周期上升沿到上升沿high_time = TIM3->CCR2+1;//硬件自動捕獲高電平時間 , 上升沿到下降沿fre_cap = main_clock / (period * (TIM3->PSC + 1));//直接計算頻率duty = ((float)high_time / period) * 100;//計算占空比}
}

發現的hal庫小細節

	//HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1 | TIM_CHANNEL_2);
//同時打開定時器三的中斷是不行的 , 需要像下面這樣一個通道一個通道的打開才能正常進入中斷HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2);

使用定時器不改變占空比的同時改變頻率的方法

思路就是直接在定時器中改變定時器的預分頻系數 , 這樣占空比就能保持不變 , 但是頻率會逐漸改變

好像使用__HAL_TIM_SET_PRESCALER(&htim15 ,prescaler);這個函數在中斷中去進行改變定時器的預分頻系數總是會卡死的 , 改成直接操作寄存器的方式似乎就可以了TIM->PSC = prescaler

定時器中斷中不要改變其他定時器的預分頻系數 ,不然會程序會直接卡死,測試了幾個其他的函數發現只有setprescaler會卡死 , 然后加上先失能全局中斷后使能全局中斷之后就正常了 , 說明可能修改的定時器的中斷函數正在運行 , 但是預分頻系數突然被改變了導致的,所以需要先關閉全局中斷 , 修改完成之后再使能全局中斷 , 也可以直接操作寄存器實現預分頻系數的修改 .
也就是加上這兩句 , 更加具體的內容可以問Ai

__disable_irq();//失能全局中斷

.........

__enable_irq();//使能全局中斷

下面是代碼 , 還有一些細節需要注意的就是ARR , CCR , PSC都是從零開始的所以需要仔細的調節一下

//按鍵部分
else if(short_press == 2){//選擇if(jiemian_mode == 0){//如果是數據界面__HAL_TIM_ENABLE_IT(&htim15, TIM_IT_UPDATE);//重新使能更新中斷
}//實現定時器的使能
就是手動開啟定時器的中斷 , 中斷中關閉定時器的中斷//定時器就15實現上升或者下降沿
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//中斷實際上是只能有一個的
{//如果是定時器15的中斷if(htim->Instance == TIM15){if(fre_mode == 0){//如果現在是低頻,需要緩慢變化到高頻if(TIM2->PSC >= 40){//40進來之后-最后一次//__disable_irq();//失能所有中斷TIM2->PSC-=1;if(TIM2->PSC <= 39){//這里也應該是減1的值fre_mode =1;__HAL_TIM_DISABLE_IT(&htim15,TIM_IT_UPDATE);//完成之后關閉更新中斷}//__enable_irq();//使能所有中斷}}else if(fre_mode == 1){//如果現在是高頻需要變化到低頻if(TIM2->PSC <= 78){//78進來再+最后一次//__disable_irq();//失能所有中斷TIM2->PSC+=1;if(TIM2->PSC >= 79){//應該是-1的值也就是80-1的值fre_mode =0;__HAL_TIM_DISABLE_IT(&htim15,TIM_IT_UPDATE);//完成之后關閉更新中斷}//__enable_irq();//使能所有中斷}}}
}

串口

重定向原理

就是更改了printf與scanf的底層函數,從另外的地址進行讀寫操作

重定向代碼

可以直接套用 ,模式配置成最異步最簡單的就可以

這個是普通的阻塞發送的串口重定向

/*--------------------串口重定向------------------*/
int fputc(int ch,FILE *f)
{
//采用輪詢方式發送1字節數據,超時時間設置為無限等待HAL_UART_Transmit(&huart2,(uint8_t *)&ch,1,HAL_MAX_DELAY);return ch;
}int fgetc(FILE *f)
{uint8_t ch;// 采用輪詢方式接收 1字節數據,超時時間設置為無限等待HAL_UART_Receive( &huart2,(uint8_t*)&ch,1, HAL_MAX_DELAY);return ch;
}

使用串口中斷進行數據接受的時候使用會使用到

HAL_UART_Receive_IT(&huart2,(uint8_t *)rec_data,num_byte);

這個函數會使能串口中斷接受然后我們在串口接受回調函數中接受數據 ,需要注意的點在于這個函數只要接收到1個字節就會觸發中斷,所以需要進行逐個字節的解析,這是比較麻煩的下面怎么利用串口的空閑中斷和DMA進行數據傳輸

配置界面如下

?

	//主函數中
HAL_UARTEx_ReceiveToIdle_DMA(&huart1 , rx_buffer , sizeof(rx_buffer));
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx , DMA_IT_HT);
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{if(huart->Instance == USART1){//如果是串口1的中斷HAL_UARTEx_ReceiveToIdle_DMA(&huart1 , rx_buffer ,sizeof(rx_buffer));sscanf(rx_buffer ,"%d-%d",&a,&b);//讀取特定字符,強大好使printf("%d-%d\n",a,b);if(rx_buffer[0]=='1'&&rx_buffer[1]=='2'&&rx_buffer[2]=='3'&&rx_buffer[3]=='\n'){light(1,1);}else if(rx_buffer[0]=='3'&&rx_buffer[1]=='2'&&rx_buffer[2]=='1'&&rx_buffer[3]=='\n'){light(1,0);}}__HAL_DMA_DISABLE_IT(&hdma_usart1_rx , DMA_IT_HT);memset(rx_buffer,0,sizeof(rx_buffer));
}void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART1){//錯誤中斷HAL_UARTEx_ReceiveToIdle_DMA(&huart1 , rx_buffer ,sizeof(rx_buffer));__HAL_DMA_DISABLE_IT(&hdma_usart1_rx , DMA_IT_HT);memset(rx_buffer,0,sizeof(rx_buffer));}
}

怎么從串口接收到的字符串數據中解析出float型的數據

其實不只是浮點數像整數型等等都是可以的

從串口接收到的字符數據中解析出我們想要的數據總共有三種方法 , 分別是使用ssacnf函數,使用atof函數以及純手動進行數據解析 ,我將講解其中的第一和第二種

首先來講講C語言中的字符串處理函數

strchr

char *strchr(const char *str, int c)

這個函數用于查找字符串中我們想要的字符 , 其中str是給定字符串 , 其中c這個字符是在str中要搜索的字符串 ,返回值是指向該字符串中第一次出現的字符的指針,不包含時返回空指針

sscanf

sscanf()這個函數與我們常見的scanf函數的區別主要是sscanf()函數從給定的字符串中讀取指定字符,而scanf函數從屏幕中讀取指定字符 ,他們的函數原型分別如下

Int sscanf( string str, string fmt, mixed var1, mixed var2 ... );//str就是給定字符串,fmt是我們要的格式 ,var是我們指定的格式字符串保存的變量

int scanf( const char *format [,argument]... );//這個不過多講了

下面的資料來自于百度百科 , 這個函數真的超級強大 ,才注意到

1、一般用法

char?buf[512]?=?;

sscanf("123456?",?"%s",?buf);

printf("%s\n",?buf);

結果為:123456

2. 取指定長度的字符串。如在下例中,取最大長度為4字節的字符串。

sscanf("123456?",?"%4s",?buf);

printf("%s\n",?buf);

結果為:1234

3. 取到指定字符為止的字符串。如在下例中,取遇到空格為止字符串。

sscanf("123456?abcdedf",?"%[^?]",?buf);

printf("%s\n",?buf);

結果為:123456

4. 取僅包含指定字符集的字符串。如在下例中,取僅包含1到9和小寫字母的字符串。

sscanf("123456abcdedfBCDEF",?"%[1-9a-z]",?buf);

printf("%s\n",?buf);

結果為:123456abcdedf

5. 取到指定字符集為止的字符串。如在下例中,取遇到大寫字母為止的字符串。

sscanf("123456abcdedfBCDEF",?"%[^A-Z]",?buf);

printf("%s\n",?buf);

結果為:123456abcdedf

6.給定一個字符串iios/12DDWDFF@122,獲取 / 和 @ 之間的字符串,先將 "iios/"過濾掉,再將非'@'的一串內容送到buf中

sscanf("iios/12DDWDFF@122",?"%*[^/]/%[^@]",?buf);

printf("%s\n",?buf);

結果為:12DDWDFF

7.給定一個字符串"hello, world",僅保留"world"。(注意:“,”之后有一空格)

sscanf("hello,?world",?"%*s%s",?buf);

printf("%s\n",?buf);

結果為:world  P.S.?%*s表示第一個匹配到的%s被過濾掉,即hello,被過濾了,如果沒有空格則結果為NULL

memset

memset是C和C++中的內存初始化函數,可以將指定內存區域設置為特定值

void *memset(void *s, int ch,?size_t?n);//函數原型

void *s是要填充的內存區域的起始地址

int ch指定了要填充到內存的值(實際填充時ch 會被轉換為unsigned char 類型)

size_t n 決定了要填充的字節數

第一種實現方法

使用時記得在keil中勾選use micro lib這個選項

strchr+sscanf實現

#include <stdio.h>  // 確保標準庫支持char rx_data[] = "k0.2\n";
float value = 0;// 查找'k'的位置并解析
char *key_ptr = strchr(rx_data, 'k');
if (key_ptr != NULL) {if (sscanf(key_ptr + 1, "%f", &value) == 1) { // +1跳過'k'// 成功解析,value = 0.2} else {// 解析失敗處理}
}
uint8_t rx_buffer[128];//接受緩存區
uint8_t rx_index = 0;
_Bool rx_complete = 0;
float new_k = 0;
//在這里我們發送的數據是k0.4\n,總共是5個字符
//中斷進行數據解析
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{if (huart->Instance == USART2){HAL_UARTEx_ReceiveToIdle_DMA(&huart2, rx_buffer, 128); //接收完畢后重啟串口DMA模式接收數據//數據處理,從字符串中解析數據變成0.4,使用字符函數實現if(rx_buffer[0] == 'k'&& rx_buffer[4] == '\n')//進行{//其中target是指向字符串中第一次出現我們想要字符的指針char* target = strchr((const char *)rx_buffer , 'k');if(target != NULL){if(sscanf((const char*)target + 1,"%f",&new_k) == 1)//解析成功的處理{light(3,1);}	else {//解析失敗的處理light(4,1);}}}//HAL_UART_Transmit(&huart2, rx_buffer, Size, 0xffff);   // 將接收到的數據再發出__HAL_DMA_DISABLE_IT(&hdma_usart2_rx, DMA_IT_HT);	   // 手動關閉DMA_IT_HT中斷memset(rx_buffer, 0, 128);							   // 清除接收緩存}
}

第二種

使用atof函數

RTC實時時鐘

RTC實時時鐘就是32內部的一個獨立的定時器 ,如果使用外部低速晶振那就是32.768khz,進行預分頻,如果是127和255,就是32768/((127+1)*(255+1)) = 1Hz

即便不需要獲取日期,也需要添加獲取日期的函數,不然時間是不會流動的

	RTC_TimeTypeDef sTime = {0};RTC_DateTypeDef sDate = {0};RTC_AlarmTypeDef sAlarm = {0};//在上面先定義結構體HAL_RTC_GetTime(&hrtc,&sTime,RTC_FORMAT_BIN);//獲取時間保存到Time結構體HAL_RTC_GetDate(&hrtc,&sDate,RTC_FORMAT_BIN);//獲取日期保存到Date結構體

如果想要手動修改RTC鬧鐘的時間可以自己再寫一個函數

void Set_RTC_Alarm(uint8_t hours ,uint8_t minutes ,uint8_t seconds)
{RTC_AlarmTypeDef sAlarm = {0};HAL_RTC_DeactivateAlarm(&hrtc, RTC_ALARM_A);//棄用舊鬧鐘//實際測試中好像不需要這兩句代碼也能正常修改,不知道為什么__HAL_RCC_PWR_CLK_ENABLE();//解除備份域時鐘保護HAL_PWR_EnableBkUpAccess();//使能備份域時鐘操作sAlarm.AlarmTime.Hours = hours;sAlarm.AlarmTime.Minutes = minutes;sAlarm.AlarmTime.Seconds = seconds;sAlarm.AlarmTime.SubSeconds = 0;sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;//這個接口是棄用的sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;sAlarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY;sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_NONE;sAlarm.AlarmDateWeekDaySel = RTC_ALARMSUBSECONDMASK_NONE;sAlarm.AlarmDateWeekDay = 1;sAlarm.Alarm = RTC_ALARM_A;HAL_RTC_SetAlarm_IT(&hrtc , &sAlarm,RTC_FORMAT_BIN);HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
}

LCD顯示

主要點在于通過定時器定時中斷設置標志為進行閃爍顯示,在中斷里面寫一個標志位 ,不斷的翻轉這個標志位的狀態實現,不同的狀態顯示不同的內容就能實現閃爍的顯示 , 或者下劃線什么的 ,這個比較簡單了

顯示一些不常見字符的方法 例如顯示百分號的方法

char baifen = '%';
sprintf(showstring,"     P:%.2f%c    ",duty,baifen);
LCD_DisplayStringLine(Line4,(uint8_t *)showstring);
//這樣就能顯示百分號了

%s占位符與%c占位符的區別

特性%c%s
操作對象單個字符 (char)字符串(字符數組/指針)
內存操作1字節連續內存直到?\0
輸入行為讀取任何字符(包括空格)跳過空白符,讀到空格停止
是否需要?&需要(scanf?中)不需要(數組名即地址)
輸出終止條件無(固定1字符)遇到?\0?停止

?C語言中 , 不要直接寫3/8這樣你會得到0 , 想要得到浮點數就寫3.0/8.0這樣計算得到浮點數

I2C讀寫操作

實際上就是E2PROM的讀寫操作的實現應當是較為簡單的 , 下面給出代碼

//0xa0是寫地址  
//0xa1是讀地址
void eeprom_write(uint8_t addr ,uint8_t dat)
{I2CStart();I2CSendByte(AT24C02_Adress);//寫哪個從機I2CWaitAck();//等待應答I2CSendByte(addr);//發送要寫地址I2CWaitAck();I2CSendByte(dat);//發送要寫的數據I2CWaitAck();I2CStop();HAL_Delay(20);//防止連續寫入時出錯
}uint8_t eeprom_read(uint8_t addr)
{I2CStart();I2CSendByte(AT24C02_Adress);//寫哪個從機I2CWaitAck();//等待應答I2CSendByte(addr);//發送要寫地址I2CWaitAck();//等待應答I2CStop();I2CStart();//重新開始是為了將控制權交給從機I2CSendByte(0xa1);//發送要讀的地址I2CWaitAck();//等待應答uint8_t data = I2CReceiveByte();//接受數據I2CSendNotAck();//發送不回應,也就是不繼續讀了I2CStop();//停止return data;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/76625.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/76625.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/76625.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Docker 鏡像、容器與數據卷的高效管理:最佳實踐與自動化腳本20250411

Docker 鏡像、容器與數據卷的高效管理&#xff1a;最佳實踐與自動化腳本 引言 在現代軟件開發中&#xff0c;容器化技術正變得越來越重要。Docker 作為容器化的代表工具&#xff0c;在各大企業中得到了廣泛的應用。然而&#xff0c;隨著容器化應用的增多&#xff0c;如何高效…

Selenium之Actions事件

鼠標、鍵盤組合鍵 在使用selenium的時候&#xff0c;有的時候我們需要鼠標單擊、雙擊、拖動&#xff1b;或者是按下鍵盤的某個鍵&#xff0c;松開某個按鍵&#xff0c;以及組合鍵的使用&#xff1b;今天我們就來看一看&#xff0c;怎么樣實現上面的操作 先把準備工作做好&…

如何在 CentOS 7 系統上以容器方式部署 GitLab,使用 ZeroNews 通過互聯網訪問 GitLab 私有倉庫,進行代碼版本發布與更新

第 1 步&#xff1a; 部署 GitLab 容器? 在開始部署 GitLab 容器之前&#xff0c;您需要創建本地目錄來存儲 GitLab 數據、配置和日志&#xff1a; #創建本地目錄 mkdir -p /opt/docker/gitlab/data mkdir -p /opt/docker/gitlab/config mkdir -p /opt/docker/gitlab/log#gi…

.py文件和.ipynb文件的區別:完整教程

一、概述 Python開發者常用的兩種文件格式.py和.ipynb各有特點&#xff0c;本教程將通過對比分析、代碼示例和場景說明&#xff0c;幫助開發者全面理解二者的區別與聯系。 二、核心區別對比 1. 文件格式本質 特性.ipynb文件.py文件文件類型JSON結構化文檔純文本文件存儲內容…

Go 字符串四種拼接方式的性能對比

簡介 使用完整的基準測試代碼文件&#xff0c;可以直接運行來比較四種字符串拼接方法的性能。 for 索引 的方式 for range 的方式 strings.Join 的方式 strings.Builder 的方式 寫一個基準測試文件 echo_bench_test.go package mainimport ("os""stri…

從代碼學習深度學習 - Bahdanau注意力 PyTorch版

文章目錄 1. 前言為什么選擇Bahdanau注意力本文目標與預備知識2. Bahdanau注意力機制概述注意力機制簡述加性注意力與乘性注意力對比Bahdanau注意力的數學原理與流程圖數學原理流程圖可視化與直觀理解3. 數據準備與預處理數據集簡介數據加載與預處理1. 讀取數據集2. 預處理文本…

19【動手學深度學習】卷積層

1. 從全連接到卷積 2. 圖像卷積 3. 圖形卷積代碼 互相關操作 import torch from torch import nn from d2l import torch as d2ldef corr2d(X, K):"""計算2維互相關運算"""h, w K.shapeY torch.zeros((X.shape[0]-h1, X.shape[1]-w 1))for …

Linux xorg-server 解析(一)- 編譯安裝Debug版本的xorg-server

一:下載代碼 1. 配置源,以Ubuntu24.04 為例( /etc/apt/sources.list.d/ubuntu.sources): 2. apt source xserver-xorg-core 二:編譯代碼 1. sudo apt build-dep ./ 2. DEB_BUILD_OPTIONS="nostrip" DEB_CFLAGS_SET="-g -O0" dpkg-buildpac…

大模型SFT用chat版還是base版 SFT后災難性遺忘怎么辦

大模型SFT用chat版還是base版 進行 SFT 時&#xff0c;基座模型選用 Chat 還是 Base 模型&#xff1f; 選 Base 還是 Chat 模型&#xff0c;首先先熟悉 Base 和 Chat 是兩種不同的大模型&#xff0c;它們在訓練數據、應用場景和模型特性上有所區別。 在訓練數據方面&#xf…

【圖像生成之21】融合了Transformer與Diffusion,Meta新作Transfusion實現圖像與語言大一統

論文&#xff1a;Transfusion: Predict the Next Token and Diffuse Images with One Multi-Modal Model 地址&#xff1a;https://arxiv.org/abs/2408.11039 類型&#xff1a;理解與生成 Transfusion模型?是一種將Transformer和Diffusion模型融合的多模態模型&#xff0c;旨…

動態多目標進化算法:基于知識轉移和維護功能的動態多目標進化算法(KTM-DMOEA)求解CEC2018(DF1-DF14)

一、KTM-DMOEA介紹 在實際工程和現實生活中&#xff0c;許多優化問題具有動態性和多目標性&#xff0c;即目標函數會隨著環境的變化而改變&#xff0c;并且存在多個相互沖突的目標。傳統的多目標進化算法在處理這類動態問題時面臨著一些挑戰&#xff0c;如收斂速度慢、難以跟蹤…

部署NFS版StorageClass(存儲類)

部署NFS版StorageClass存儲類 NFS版PV動態供給StorageClass(存儲類)基于NFS實現動態供應下載NFS存儲類資源清單部署NFS服務器為StorageClass(存儲類)創建所需的RBAC部署nfs-client-provisioner的deployment創建StorageClass使用存儲類創建PVC NFS版PV動態供給StorageClass(存儲…

Vue使用el-table給每一行數據上面增加一行自定義合并行

// template <template><el-table:data"flattenedData":span-method"objectSpanMethod"borderclass"custom-header-table"style"width: 100%"ref"myTable":height"60vh"><!-- 訂單詳情列 -->&l…

vue項目使用html2canvas和jspdf將頁面導出成PDF文件

一、需求&#xff1a; 頁面上某一部分內容需要生成pdf并下載 二、技術方案&#xff1a; 使用html2canvas和jsPDF插件 三、js代碼 // 頁面導出為pdf格式 import html2Canvas from "html2canvas"; import jsPDF from "jspdf"; import { uploadImg } f…

大模型LLM表格報表分析:markitdown文件轉markdown,大模型markdown統計分析

整體流程&#xff1a;用markitdown工具文件轉markdown&#xff0c;然后大模型markdown統計分析 markitdown https://github.com/microsoft/markitdown 在線體驗&#xff1a;https://huggingface.co/spaces/AlirezaF138/Markitdown 安裝&#xff1a; pip install markitdown…

Linux 第二講 --- 基礎指令(二)

前言 這是基礎指令的第二部分&#xff0c;但是該部分的講解會大量使用到基礎指令&#xff08;一&#xff09;的內容&#xff0c;為了大家的觀感&#xff0c;如果對Linux的一些基本指令不了解的話&#xff0c;可以先看基礎指令&#xff08;一&#xff09;&#xff0c;同樣的本文…

python格式化字符串漏洞

什么是python格式化字符串漏洞 python中&#xff0c;存在幾種格式化字符串的方式&#xff0c;然而當我們使用的方式不正確的時候&#xff0c;即格式化的字符串能夠被我們控制時&#xff0c;就會導致一些嚴重的問題&#xff0c;比如獲取敏感信息 python常見的格式化字符串 百…

LLaMA-Factory雙卡4090微調DeepSeek-R1-Distill-Qwen-14B醫學領域

unsloth單卡4090微調DeepSeek-R1-Distill-Qwen-14B醫學領域后&#xff0c;跑通一下多卡微調。 1&#xff0c;準備2卡RTX 4090 2&#xff0c;準備數據集 醫學領域 pip install -U huggingface_hub export HF_ENDPOINThttps://hf-mirror.com huggingface-cli download --resum…

React Hooks: useRef,useCallback,useMemo用法詳解

1. useRef&#xff08;保存引用值&#xff09; useRef 通常用于保存“不會參與 UI 渲染&#xff0c;但生命周期要長”的對象引用&#xff0c;比如獲取 DOM、保存定時器 ID、WebSocket等。 新建useRef.js組件&#xff0c;寫入代碼&#xff1a; import React, { useRef, useSt…

Spring AI 結構化輸出詳解

一、Spring AI 結構化輸出的定義與核心概念 Spring AI 提供了一種強大的功能&#xff0c;允許開發者將大型語言模型&#xff08;LLM&#xff09;的輸出從字符串轉換為結構化格式&#xff0c;如 JSON、XML 或 Java 對象。這種結構化輸出能力對于依賴可靠解析輸出值的下游應用程…