使用STM32CubeMX使用CAN驅動無刷電機DJI3508

簡介

文章為筆記性質

硬件包括

大疆C板

電機調速器C620

DJI3508電機

CAN知識介紹

CAN的概念

CAN是控制器區域網絡(Controller Area?Network)的縮寫。CAN總線是一種適用于工業設備的高性能總線網絡。說白了就是也就是一種通訊方式而已。

把多個設備串在一根線上,再通過一定的協議約束,就能實現最方便的多設備互相通信。

而不用每個設備都兩兩之間有連線,大大節省了成本和布線復雜程度。

其次CAN的可靠性也比較高,采用差分線的形式,具有較強的抗干擾能力和噪聲抑制能力。

CAN總線結構

CAN總線網絡的結構有閉環和開環兩種形式。

閉環

閉環結構的CAN總線網絡,總線兩端各接一個120Ω的終端電阻,兩根信號線形成的回路。這種CAN總線網絡由ISO11898標準定義,是高速短距離的CAN網絡,通信速率為125kbit/s到1Mbit/s,在1Mbit/s通信速率時,總線長達40m。

開環

開環結構的CAN總線網絡,兩根信號線獨立,各自串聯一個2.2 kΩ的電阻。這中CAN總線網絡由ISO11519-2標準定義,是低速遠距離的CAN網絡,最高速率為125kbit/s。在40kbit/速率時,總線最長舉例為1000m。
??閉環結構適用于節點數量有限的小型網絡、簡單性要求高、可靠性要求高的場景。開環結構適用于大型網絡,擴展性要求高,故障隔離要求高的場景。

一般情況下,(平時驅動個電機、進行簡單的通信),我們只用得到 高速CAN 也就是 閉環CAN

所以這里也就主要說一說高速CAN(閉環CAN,1Mbit/s)

以下部分簡稱之為CAN

下圖是CAN的結構示意圖:

CAN總線上只有兩根信號線,即CANHCANL,沒有時鐘同步信號。

所以CAN是一種異步通信方式,與UART的異步通信方式類似,而SPI、I2C是以時鐘信號同步的同步通信方式

其中的120Ω電阻被稱為終端電阻,作用是:阻抗匹配、減少信號反射、增強抗干擾等。

CAN的物理層

? ??????CAN總線的兩根信號線通常采用雙絞線,傳輸的是差分信號,通過兩根信號線的電壓差CANH - CANL來表示CAN的總線電平。

????????以差分信號傳輸信號具有抗干擾強,能有效抑制外部電子干擾等優點,這也是CAN總線在工業上應用廣泛的一個原因。
??兩根信號線的電壓差CANH-CANL表示CAN總線的電平,與傳輸的邏輯信號1和邏輯0對應。對于邏輯1的稱為隱形(Ressive)電平,對應與邏輯0的稱為顯性(Dominant)電平。

到這里應該對CAN有了一定的了解了,那么你也許會問:

單片機只能傳輸電平信號,怎么實現這種操作呢?

所以我們就需要另一個芯片作為CAN收發器 把單片機的高低電平信號轉化為 CAN總線需要的信號

CAN收發器一般是單獨的芯片,并且根據CAN總線的結構不同,需要使用不同的CAN收發器芯片,常見的CAN收發器芯片有TJA1050,TJA1042,SIT1050T,SIT1050T支持高速CAN,傳輸速率可達1Mbps。

我們的C板上已經有了CAN收發器芯片,所以直接用就可以。

CAN的數據傳輸特點

(1)CAN總線上的節點既可以發送數據又可以接收數據,沒有主從之分。但是在同一個時刻,只能有一個節點在發送數據,其他節點只能接收數據


(2) CAN總線上的節點沒有地址的概念。CAN總線上的數據是以為單位傳輸的。,幀又分為數據幀、遙控幀等多種幀類型,幀包含需要傳輸的數據或控制信息。


(3)CAN總線具有“線與”的特性,也就是當有兩個字節同時向總線發送信號時,一個發送顯性電平(邏輯0),另一個發送隱形電平(邏輯1),總線呈現為顯性電平,這個特性被用于總線仲裁,也就是哪個節點優先占用總線進行發送操作。

????????當沒有數據發送時,終端電阻使總線保持在隱性電平。
(4)每個幀也有一個標識符(Indentifier,以下簡稱ID)。ID不是地址,它表示數據傳輸的類型,也可以用于總線仲裁時確定優先級。例如,在汽車的CAN總線上,假設用于碰撞檢測的節點輸出數據幀的ID為01,車內溫度檢測節點發送數據幀的ID為05等。


(5) 每個CAN節點都接收數據,但是可以對接收的幀ID進行過濾。只有節點需要的數據才會被接收并進一步處理,不需要的數據會被自動舍棄。例如,假設安全氣囊控制器只接收碰撞檢測節點發出的ID為01的幀,這種ID的過濾是由硬件完成的,以便安全氣囊控制器在發生碰撞時能及時響應。


(6)CAN總線通信是半雙工的,即總線不能同時發送和接收。在多個節點競爭總線進行發送時,通過ID的優先級進行仲裁,競爭勝出的節點繼續發送,競爭失敗的節點立刻轉為接送狀態。


(7)CAN總線沒有用于同步的時鐘信號,所以需要規定CAN總線通信的波特率,所有節點都必須使用相同的波特率進行通訊。

CAN協議層

CAN總線以“幀”形式進行通信。

CAN協議定義了5種類型的幀:數據幀、遙控幀、錯誤幀、過載幀、間隔幀,其中數據幀最為常用。


??其中,數據幀和遙控幀有ID,并且有標準格式和拓展格式兩種格式,標準格式的ID是11位,拓展格式的ID是29位,下面僅詳細介紹數據幀的結構,其他幀的結構可參考相關資料。

數據幀可以分為以下幾段。
??(1)幀起始(Start Of Frame,SOF)。幀起始只有一個位,是一個顯性電平(邏輯0),表示一個幀的開始。
??(2)仲裁段(Arbitration Field)。仲裁段包括11位的ID和RTR位,共12位。多個節點競爭總線發送數據時,根據仲裁段的數據決定哪個節點優先占用總線。哪個ID先出現顯性電平(邏輯0),對應的節點就占用總線。所以,ID數值越小的優先級更高(CAN總線使用的是基于標識符的仲裁機制,標識符中的低位具有較高的權重,因此ID數值小的節點的標識符具有更高的優先級)。如果兩個節點發送數據幀的ID相同,再根據仲裁段最后的RTR位進行裁決。
??RTR(Remote Transmit Request)是遠程傳輸請求位,RTR位用于區分數據幀和遙控幀。數據幀的RTR位是顯性電平(邏輯0),遙控幀的RTR位是隱形電平(邏輯1)。所以,具有相同ID的數據幀和遙控幀競爭總線時,數據幀優先級更高。
??(3)控制段。控制段包括IDE位、RB0位和4位的DLC,共6位。
IDE是標識符擴展位(Identifier Extension Bit),用于表示幀是擴展格式還是標準格式。標準格式的IDE是顯性電平(邏輯0),拓展格式幀的IDE是隱形電平(邏輯1)。
??RB0是保留位,默認為顯性電平。
??DLC是4個位的數據長度編碼(Data Length Code),編碼數值為0到8,表示后面數據段的字節,遙控幀的DLC編碼數值為0,因為遙控幀不傳輸數據。
??(4)數據段。數據段里面是數據幀需要傳輸的數據,可以是0到8字節,數據的字節個數由DLC編碼確定。遙控幀沒有數據段。
??(5)CRC段。檢查幀的傳輸錯誤的段。CRC共16位,其中前15位是CRC校驗碼,最后一位總是隱形電平,是CRC段的界定符(Delimiter)。
??(6)ACK段。ACK段包括一個ACK位(Acknowledge Bit)和一個ACK段界定符。發送節點發送的ACK位是隱形電平,接收節點接收的ACK位是顯性電平。
??(7)幀結束(End Of Frame,EOF)。幀結束是幀結束段,由7個隱性位表示EOF。
??數據幀或遙控幀結束后,后面一般是幀空間或過載幀,用于分隔開數據幀和遙控幀。

具體參考下圖:

到這里基本知識已經了解的差不多了。

更詳細的內容請看中科大RM的培訓視頻。

接下來看CubeMX配置和相應代碼

CubeMX配置

在SYS把DEBUG改成sw

在RCC開啟外部時鐘

配置時鐘樹

這里需要尤其關注 input frequence 一定一定要是12M 因為C板的硬件給的就是12M晶振,如果你這里配錯了 接下來的到板子上的時鐘就亂了 就不是你想要的頻率了。

然后是開啟CAN1

把波特率調整為1M

Normal模式

開NVIC

OK到這就配置完成。

代碼

本代碼基于中科大RM電控培訓的開源代碼改編而來

包括接收和發送

一些宏定義和聲明

一些必要的宏定義以及聲明// 濾波器編號
#define CAN_FILTER(x) ((x) << 3)// 接收隊列
#define CAN_FIFO_0 (0 << 2)
#define CAN_FIFO_1 (1 << 2)// 標準幀或擴展幀
#define CAN_STDID (0 << 1)
#define CAN_EXTID (1 << 1)// 數據幀或遙控幀
#define CAN_DATA_TYPE 0
#define CAN_REMOTE_TYPE 1/*** @brief CAN接收的信息結構體**/
typedef struct
{CAN_RxHeaderTypeDef Header;uint8_t Data[8];
} Struct_CAN_Rx_Buffer;/*** @brief CAN通信接收回調函數數據類型**/
typedef void (*CAN_Call_Back)(Struct_CAN_Rx_Buffer *);/*** @brief CAN通信處理結構體**/
typedef struct
{CAN_HandleTypeDef *CAN_Handler;Struct_CAN_Rx_Buffer Rx_Buffer;CAN_Call_Back Callback_Function;
} Struct_CAN_Manage_Object;// CAN通信發送緩沖區
uint8_t CAN1_0x0a0_Tx_Data[8];
uint8_t CAN1_0x1ff_Tx_Data[8];
uint8_t CAN1_0x200_Tx_Data[8];
uint8_t CAN1_0x2ff_Tx_Data[8];
uint8_t CAN1_0xxf1_Tx_Data[8];
uint8_t CAN1_0xxf2_Tx_Data[8];
uint8_t CAN1_0xxf3_Tx_Data[8];
uint8_t CAN1_0xxf4_Tx_Data[8];
uint8_t CAN1_0xxf5_Tx_Data[8];
uint8_t CAN1_0xxf6_Tx_Data[8];
uint8_t CAN1_0xxf7_Tx_Data[8];
uint8_t CAN1_0xxf8_Tx_Data[8];

這里創建一個兩個CAN的結構體用于接收數據


Struct_CAN_Manage_Object CAN1_Manage_Object = {0};
Struct_CAN_Manage_Object CAN2_Manage_Object = {0};

CAN過濾器

/*** @brief 配置CAN的過濾器** @param hcan CAN編號* @param Object_Para 篩選器編號0-27 | FIFOx | ID類型 | 幀類型* @param ID 驗證碼* @param Mask_ID 屏蔽碼(0x3ff, 0x1fffffff)*/
void can_filter_mask_config(CAN_HandleTypeDef *hcan, uint8_t Object_Para, uint32_t ID, uint32_t Mask_ID)
{// 檢測傳參是否正確assert_param(hcan != NULL);// CAN過濾器初始化結構體CAN_FilterTypeDef can_filter_init_structure;// 濾波器序號, 0-27, 共28個濾波器can_filter_init_structure.FilterBank = Object_Para >> 3;// 濾波器模式,設置ID掩碼模式can_filter_init_structure.FilterMode = CAN_FILTERMODE_IDMASK;if ((Object_Para & 0x02)){// 29位 拓展幀//  32位濾波can_filter_init_structure.FilterScale = CAN_FILTERSCALE_32BIT;// 驗證碼 高16bitcan_filter_init_structure.FilterIdHigh = (ID << 3) >> 16;// 驗證碼 低16bitcan_filter_init_structure.FilterIdLow = ID << 3 | (Object_Para & 0x03) << 1;// 屏蔽碼 高16bitcan_filter_init_structure.FilterMaskIdHigh = (Mask_ID << 3) >> 16;// 屏蔽碼 低16bitcan_filter_init_structure.FilterMaskIdLow = Mask_ID << 3 | (0x03) << 1;}else{// 11位 標準幀//  32位濾波can_filter_init_structure.FilterScale = CAN_FILTERSCALE_16BIT;// 標準幀驗證碼 高16bit不啟用can_filter_init_structure.FilterIdHigh = 0x0000;// 驗證碼 低16bitcan_filter_init_structure.FilterIdLow = ID << 5 | (Object_Para & 0x02) << 4;// 標準幀屏蔽碼 高16bit不啟用can_filter_init_structure.FilterMaskIdHigh = 0x0000;// 屏蔽碼 低16bitcan_filter_init_structure.FilterMaskIdLow = (Mask_ID << 5) | 0x01 << 4;}// 濾波器綁定FIFO0或FIFO1can_filter_init_structure.FilterFIFOAssignment = (Object_Para >> 2) & 0x01;// 從機模式選擇開始單元 , 前14個在CAN1, 后14個在CAN2can_filter_init_structure.SlaveStartFilterBank = 14;// 使能濾波器can_filter_init_structure.FilterActivation = ENABLE;// 過濾器配置if (HAL_CAN_ConfigFilter(hcan, &can_filter_init_structure) != HAL_OK){Error_Handler();}
}

CAN初始化函數

/*** @brief 初始化CAN總線** @param hcan CAN編號* @param Callback_Function 處理回調函數*/
void CAN_Init(CAN_HandleTypeDef *hcan, CAN_Call_Back Callback_Function)
{if (hcan->Instance == CAN1){CAN1_Manage_Object.CAN_Handler = hcan;CAN1_Manage_Object.Callback_Function = Callback_Function;//         can_filter_mask_config(hcan, CAN_FILTER(0) | CAN_FIFO_0 | CAN_STDID | CAN_DATA_TYPE, 0x200 ,0x7F8);  //只接收0x200-0x207//         can_filter_mask_config(hcan, CAN_FILTER(1) | CAN_FIFO_1 | CAN_STDID | CAN_DATA_TYPE, 0x200, 0x7F8);
//        can_filter_mask_config(hcan, CAN_FILTER(0) | CAN_FIFO_0 | CAN_STDID | CAN_DATA_TYPE, 0x200, 0x7F8);
//        can_filter_mask_config(hcan, CAN_FILTER(1) | CAN_FIFO_1 | CAN_STDID | CAN_DATA_TYPE, 0x200, 0x7F8);can_filter_mask_config(hcan, CAN_FILTER(0) | CAN_FIFO_0 | CAN_STDID | CAN_DATA_TYPE, 0, 0); // 只接收can_filter_mask_config(hcan, CAN_FILTER(1) | CAN_FIFO_1 | CAN_STDID | CAN_DATA_TYPE, 0, 0);}else if (hcan->Instance == CAN2){CAN2_Manage_Object.CAN_Handler = hcan;CAN2_Manage_Object.Callback_Function = Callback_Function;can_filter_mask_config(hcan, CAN_FILTER(14) | CAN_FIFO_0 | CAN_STDID | CAN_DATA_TYPE, 0, 0); // 只接收can_filter_mask_config(hcan, CAN_FILTER(15) | CAN_FIFO_1 | CAN_STDID | CAN_DATA_TYPE, 0, 0);}/*離開初始模式*/HAL_CAN_Start(hcan);/*開中斷*/HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING); // can 接收fifo 0不為空中斷HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO1_MSG_PENDING); // can 接收fifo 1不為空中斷
}

在這個函數里面利用我們的結構體到HAL的庫的底層,分別初始化了CAN1 CAN2,

然后 用了濾波器 (一定要加上這個濾波器要不然收不到數據的)

這個濾波器里面寫了起始段 仲裁段 控制段 的內容

CAN發送數據函數

/*** @brief 發送數據幀** @param hcan CAN編號* @param ID ID* @param Data 被發送的數據指針* @param Length 長度* @return uint8_t 執行狀態*/
uint8_t CAN_Send_Data(CAN_HandleTypeDef *hcan, uint16_t ID, uint8_t *Data, uint16_t Length)
{CAN_TxHeaderTypeDef tx_header;uint32_t used_mailbox;// 檢測傳參是否正確assert_param(hcan != NULL);tx_header.StdId = ID;tx_header.ExtId = 0;tx_header.IDE = 0;tx_header.RTR = 0;tx_header.DLC = Length;return (HAL_CAN_AddTxMessage(hcan, &tx_header, Data, &used_mailbox));
}

CAN報文回調函數

/*** @brief CAN報文回調函數** @param Rx_Buffer CAN接收的信息結構體*/
void CAN_Motor_Call_Back(Struct_CAN_Rx_Buffer *Rx_Buffer)
{uint8_t *Rx_Data = Rx_Buffer->Data;switch (Rx_Buffer->Header.StdId){//3508電機會傳數據回來,之前發送的ID是0x200//所以接收的ID就分別是0x201,0x202,0x203,0x204,0x205,0x206,0x207,0x208//電機的ID是幾對應的就是0x20幾//要注意的是 電機ID1-4對應0x200 ID5-8對應0x1ffcase (0x202):{Rx_Encoder = (Rx_Data[0] << 8) | Rx_Data[1];Rx_Omega = (Rx_Data[2] << 8) | Rx_Data[3];Rx_Torque = (Rx_Data[4] << 8) | Rx_Data[5];Rx_Temperature = Rx_Data[6];}break;}
}

HAL庫CAN接收FIFO中斷

/*** @brief HAL庫CAN接收FIFO0中斷** @param hcan CAN編號*/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{// CAN_RxHeaderTypeDef header;// uint8_t data;// HAL_CAN_GetRxMessage(hcan, CAN_FILTER_FIFO0, &header, &data);//選擇回調函數if (hcan->Instance == CAN1){HAL_CAN_GetRxMessage(hcan, CAN_FILTER_FIFO0, &CAN1_Manage_Object.Rx_Buffer.Header, CAN1_Manage_Object.Rx_Buffer.Data);CAN1_Manage_Object.Callback_Function(&CAN1_Manage_Object.Rx_Buffer);}else if (hcan->Instance == CAN2){HAL_CAN_GetRxMessage(hcan, CAN_FILTER_FIFO0, &CAN2_Manage_Object.Rx_Buffer.Header, CAN2_Manage_Object.Rx_Buffer.Data);CAN2_Manage_Object.Callback_Function(&CAN2_Manage_Object.Rx_Buffer);}}/*** @brief HAL庫CAN接收FIFO1中斷** @param hcan CAN編號*/
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan)
{// 選擇回調函數if (hcan->Instance == CAN1){HAL_CAN_GetRxMessage(hcan, CAN_FILTER_FIFO1, &CAN1_Manage_Object.Rx_Buffer.Header, CAN1_Manage_Object.Rx_Buffer.Data);CAN1_Manage_Object.Callback_Function(&CAN1_Manage_Object.Rx_Buffer);}else if (hcan->Instance == CAN2){HAL_CAN_GetRxMessage(hcan, CAN_FILTER_FIFO1, &CAN2_Manage_Object.Rx_Buffer.Header, CAN2_Manage_Object.Rx_Buffer.Data);CAN2_Manage_Object.Callback_Function(&CAN2_Manage_Object.Rx_Buffer);}
}

以上就是所有的偏底層一些的代碼了

接下來我們直接調用就可以了

主函數

#include "main.h"
#include "can.h"
#include "gpio.h"#include "drv_can.h"int16_t torque = 500;/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_CAN1_Init();/* USER CODE BEGIN 2 */CAN_Init(&hcan1, CAN_Motor_Call_Back);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){CAN1_0x200_Tx_Data[2] = (torque >> 8) & 0xFF;CAN1_0x200_Tx_Data[3] = torque & 0xFF;CAN_Send_Data(&hcan1,0x200,CAN1_0x200_Tx_Data,8);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}

大疆電機的控速是直接給電流的

他有一個映射的范圍

我們的傳過去的數據是2字節的所以采用

CAN1_0x200_Tx_Data[2] = (torque >> 8) & 0xFF;
CAN1_0x200_Tx_Data[3] = torque & 0xFF;

的方法來寫入。

根據3508電機的使用手冊,要根據不同的ID來往不同的位里面寫數據。

總結

總結起來,如果我們不看底層的話,實際上要我們調用的函數其實很少。

就跑一個初始化CAN_Init

把數據寫入相對應的數組內

再用CAN_Send_Data發送出去就好了

然后我們也可以在CAN_Motor_Call_Back這個回調函數中接收電調發來的消息,進行一些其他的處理等等。



本文參考鏈接如下:

中科大RM電控合集

HAL庫STM32常用外設—— CAN通信(一)_stm32 hal can-CSDN博客

趨近于完美的通訊 CAN總線!4分鐘看懂!

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

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

相關文章

Wi-Fi 802.11s自組網/EasyMesh自組網/802.11ah物聯網

一、前期調研結論 前面詳細探討了自組網和5G無線通信網絡、WiFi無線通信網絡的差異&#xff1a; 自組網 v.s 5G v.s WiFi-CSDN博客 從“分級道路”角度理解無線通信網絡拓撲包括從當前工業應用場景具體案例了解終端無線通信網絡&#xff1a; 5G無線通信網絡場景&#xff08;…

【基于hyperledger fabric的教育證書管理系統】

教育證書管理系統 系統概述 項目背景 隨著數字化轉型的深入推進&#xff0c;教育證書作為個人學術成就和專業資質的重要憑證&#xff0c;在就業市場、高等教育和職業發展中扮演著關鍵角色。然而&#xff0c;傳統教育證書管理體系面臨著數據孤島、證書偽造、驗證流程繁瑣以及跨機…

【Flask】測試平臺開發,集成禪道

概述&#xff1a; 由于公司多數測試人員還是在使用禪道&#xff0c;為了方便&#xff0c;就將禪道直接集成在我們的測試平臺中 一般可以有幾種實現方法 調用禪道的API集成集成本地部署的禪道-可能有跨域問題&#xff0c;需要解決 由于我這里已經部署了一臺本地的禪道系統&…

《UE5_C++多人TPS完整教程》學習筆記45 ——《P46 待機與跳躍(Idle And Jumps)》

本文為B站系列教學視頻 《UE5_C多人TPS完整教程》 —— 《P46 待機與跳躍&#xff08;Idle And Jumps&#xff09;》 的學習筆記&#xff0c;該系列教學視頻為計算機工程師、程序員、游戲開發者、作家&#xff08;Engineer, Programmer, Game Developer, Author&#xff09; St…

用html+js下拉菜單的demo,當鼠標點擊后展開,鼠標點擊別的地方后折疊

使用html js實現下拉菜單demo&#xff0c;因為copy的網站菜單功能失效&#xff0c;就需要自己寫一個邏輯&#xff0c;點擊其他區域折疊菜單&#xff0c;可以參考&#xff1a;<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF…

OpenCV 核心技術:顏色檢測與幾何變換實戰

在計算機視覺任務中&#xff0c;顏色空間轉換和圖像幾何變換是兩大基礎且高頻的操作 —— 前者用于精準分割特定顏色目標&#xff08;如交通信號燈、物體追蹤&#xff09;&#xff0c;后者用于調整圖像的尺寸、位置和視角&#xff08;如文檔矯正、圖像拼接&#xff09;。本文將…

[HFCTF2020]EasyLogin

文章目錄TRYWP總結TRY 注冊admin報錯username wrong。 隨便注冊一個用戶點擊GetFlag&#xff0c;permission deny。 猜測可能是需要admin權限。 看cookie發現有&#xff1a; sses:aok&#xff1a;eyJ1c2VybmFtZSI6ImEiLCJfZXhwaXJlIjoxNzU2NDU1NjczMTAxLCJfbWF4QWdlIjo4NjQwM…

Java接口和抽象類的區別,并舉例說明

Java接口和抽象類是面向對象編程中實現抽象的兩種機制&#xff0c;它們在語法、設計目的和使用場景上有顯著區別&#xff1a;一、核心區別?定義方式?抽象類&#xff1a;使用abstract class聲明&#xff0c;可包含抽象方法和具體方法45。接口&#xff1a;使用interface聲明&am…

docker-相關筆記

1: 導入鏡像 docker load -i myimage.tar# 導出鏡像 docker save myimage:latest > myimage.tar # 導入鏡像 docker load -i myimage.tardocker load -i <文件> 功能&#xff1a;用于導入通過 docker save 命令導出的鏡像歸檔文件&#xff08;通常是 .tar 格式&#…

自然語言提取PDF表格數據

自然語言提取PDF表格數據PDF v8.2的文檔解決方案與OpenAI實現了無縫的AI集成&#xff0c;可將非結構化PDF轉換為可用數據。MESCIUS 推出的 PDF 文檔解決方案 (DsPdf) 是一款軟件開發工具包&#xff0c;它提供了 .NET Core 庫和一個 JavaScript PDF 查看器&#xff0c;用于處理和…

飛牛Nas每天定時加密數據備份到網盤,基于restic的Backrest筆記分享

1. 前言 受前輩“RAID≠備份”的經驗&#xff0c;也考慮到硬盤故障時 RAID 重建步驟繁瑣&#xff0c;我干脆放棄陣列&#xff0c;直接單盤運行。 重要數據則加密后上傳至大廠云盤&#xff1a;一方面文件對外不可讀&#xff0c;規避掃描和諧&#xff1b;另一方面依靠大廠的數據安…

C#連接SQL-Server數據庫超詳細講解以及防SQL注入

C#連接SQL Server數據庫完整指南&#xff0c;整合了ADO.NET原生連接與Entity Framework Core兩種實現方式。這篇文件詳細介紹C#代碼連接數據庫的通用操作數據庫鏈接功能 數據庫的增刪改查操作1 配置全局數據庫鏈接字符串 App.config2 獲取數據庫鏈接字符串先在App.config配置連…

Pico2?ICE FPGA 開發板:從開箱到跑通示例的全歷程

FPGA 和 MCU 結合的開發板不多&#xff0c;而 Pico2?ICE 則把小巧、靈活和易上手完美結合。搭載 RP2350 雙核 RISC-V MCU Lattice iCE40UP5K FPGA&#xff0c;配合官方 SDK&#xff0c;你可以一步步跑通各種示例&#xff0c;從 LED 到 VGA&#xff0c;再到 MCU 與 FPGA 協作應…

Java圖形圖像處理【Swing圖像拖拽】【五】

Java圖形圖像處理【Swing圖像拖拽】 18.3.3 Swing圖像對象拖拽功能 18.3.3 Swing圖像對象拖拽功能 上文討論的是java.awt.dnd包中提供的拖拽API接口&#xff0c;也可稱之為AWT組件的拖拽功能。下面我們要討論的是Swing框架的拖拽功能&#xff1a;Swing組件也提供了對拖拽功能的…

狀態模式與幾個經典的C++例子

1. 狀態模式定義與核心思想 狀態模式解決的是當控制一個對象狀態轉換的條件表達式過于復雜時的情況。通過將狀態的判斷邏輯轉移到表示不同狀態的一系列類中,可以把復雜的判斷邏輯簡化。 核心思想: 狀態抽象:將對象的每個狀態都封裝到一個獨立的類中。 委托代替條件判斷:…

SyncBackPro 備份及同步軟件中的腳本功能簡介

腳本提供了一種靈活而簡單的方法來自動執行任務和擴展應用程序的功能。腳本是一個小程序&#xff0c;能夠自定義和控制備份作業。例如&#xff0c;用戶可以編寫腳本來復制、重命名或刪除特定文件、自定義用戶界面或更改配置文件設置。SyncBackPro 的腳本功能類似于 Microsoft O…

部署2.516.2版本的jenkins,同時適配jdk8

&#x1f4cc; 前言 在企業級開發中&#xff0c;我們常常面臨 新老項目并存 的復雜局面&#xff1a; 老項目基于 JDK 8 開發&#xff0c;短期內無法升級&#xff1b; 新項目采用 JDK 17&#xff08;LTS&#xff09;甚至更高版本&#xff1b; 而作為 CI/CD 核心的 Jenkins&#…

Autodesk Maya 2026.2 全新功能詳解:MotionMaker AI 動畫、LookdevX 材質增強、USD 工作流優化

軟件介紹 Autodesk Maya 2026.2是一款專業的3D計算機圖形軟件&#xff0c;它為數字內容創作者提供了豐富的工具集&#xff0c;以實現高質量的建模、動畫、模擬和渲染。該版本帶來了多項性能優化和工作流程改進&#xff0c;特別是針對生成式動畫工具MotionMaker進行了重大升級&…

STM32之DMA詳解

一、DMA 1. DMA的引入 在嵌入式系統或計算機系統中&#xff0c;數據的傳輸和處理是非常重要的操作。以下通過一個簡單的示例來展示傳統數據操作方式與 DMA 引入的必要性&#xff1a; int a 10; int b 20;a b;上述代碼包含了變量定義、初始化以及變量數據賦值操作。在傳統…

鏈表有環找入口節點原理

環形鏈表入口檢測算法數學解釋 數學原理假設定義: 鏈表頭到環入口的距離為 a環入口到相遇點的距離為 b相遇點到環入口的距離為 c環的長度為 b c第一次相遇時: 慢指針走了 s a b 步快指針走了 f a b n(b c) 步&#xff0c;其中 n 是快指針在環內轉的圈數由于快指針速度是…