[實戰] linux驅動框架與驅動開發實戰

linux驅動框架與驅動開發實戰

  • Linux驅動框架與驅動開發實戰
    • 一、Linux驅動框架概述
      • 1.1 Linux驅動的分類
      • 1.2 Linux驅動的基本框架
    • 二、Linux驅動關鍵API詳解
      • 2.1 模塊相關API
      • 2.2 字符設備驅動API
      • 2.3 內存管理API
      • 2.4 中斷處理API
      • 2.5 PCI設備驅動API
    • 三、Xilinx XDMA驅動開發詳解
      • 3.1 XDMA概述
      • 3.2 XDMA驅動開發步驟
        • 步驟1:定義PCI設備ID
        • 步驟2:定義驅動主結構體
        • 步驟3:實現PCI probe函數
        • 步驟4:實現文件操作接口
        • 步驟5:實現中斷處理
        • 步驟6:實現DMA傳輸
        • 步驟7:實現remove函數
        • 步驟8:定義PCI驅動結構體并注冊
      • 3.3 步驟總結
    • 四、XDMA驅動測試與調試
      • 4.1 加載驅動模塊
      • 4.2 測試DMA傳輸
      • 4.3 常見問題調試
    • 五、性能優化技巧
      • 5.1 使用分散/聚集DMA
      • 5.2 實現零拷貝
      • 5.3 使用DMA池
    • 六、總結

Linux驅動框架與驅動開發實戰

一、Linux驅動框架概述

Linux驅動是操作系統內核與硬件設備之間的橋梁,它使得硬件設備能夠被操作系統識別和管理。Linux內核提供了一套完善的驅動框架,開發者可以基于這些框架開發各種硬件設備的驅動程序。

1.1 Linux驅動的分類

Linux驅動主要分為以下幾類:

  1. 字符設備驅動:以字節流形式進行數據讀寫,如鍵盤、鼠標等
  2. 塊設備驅動:以數據塊為單位進行讀寫,如硬盤、SSD等
  3. 網絡設備驅動:用于網絡通信的設備,如網卡
  4. 其他特殊類型:如USB驅動、PCI驅動等框架驅動

Linux驅動模型分層:

用戶空間
系統調用接口
VFS虛擬文件系統
字符設備驅動
塊設備驅動
硬件設備

1.2 Linux驅動的基本框架

無論哪種類型的驅動,Linux都提供了相應的框架和接口。一個典型的Linux驅動包含以下組成部分:

  1. 模塊加載和卸載函數module_init()module_exit()
  2. 文件操作接口file_operations結構體
  3. 設備注冊與注銷register_chrdev()等函數
  4. 中斷處理request_irq()和中斷處理函數
  5. 內存管理kmalloc(), ioremap()等函數
  6. 同步機制:自旋鎖、信號量、互斥鎖等

二、Linux驅動關鍵API詳解

2.1 模塊相關API

module_init(init_function);  // 指定模塊加載時執行的函數
module_exit(exit_function);  // 指定模塊卸載時執行的函數
MODULE_LICENSE("GPL");       // 聲明模塊許可證
MODULE_AUTHOR("Author");     // 聲明模塊作者
MODULE_DESCRIPTION("Desc"); // 聲明模塊描述

2.2 字符設備驅動API

// 注冊字符設備
int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops);// 注銷字符設備
void unregister_chrdev(unsigned int major, const char *name);// 文件操作結構體
struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);int (*open) (struct inode *, struct file *);int (*release) (struct inode *, struct file *);// 其他操作...
};

2.3 內存管理API

// 內核內存分配
void *kmalloc(size_t size, gfp_t flags);
void kfree(const void *objp);// 物理地址映射
void *ioremap(phys_addr_t offset, unsigned long size);
void iounmap(void *addr);// 用戶空間與內核空間數據拷貝
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n);

2.4 中斷處理API

// 申請中斷
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev);// 釋放中斷
void free_irq(unsigned int irq, void *dev_id);// 中斷處理函數原型
irqreturn_t irq_handler(int irq, void *dev_id);

2.5 PCI設備驅動API

// PCI設備ID表
static const struct pci_device_id ids[] = {{ PCI_DEVICE(VENDOR_ID, DEVICE_ID) },{ 0, }
};
MODULE_DEVICE_TABLE(pci, ids);// PCI驅動結構體
static struct pci_driver pci_driver = {.name = "xdma_driver",.id_table = ids,.probe = xdma_probe,.remove = xdma_remove,// 其他回調...
};// 注冊PCI驅動
pci_register_driver(&pci_driver);// 注銷PCI驅動
pci_unregister_driver(&pci_driver);

三、Xilinx XDMA驅動開發詳解

3.1 XDMA概述

Xilinx DMA (XDMA) 是一種高性能的DMA控制器,用于在FPGA和主機內存之間傳輸數據。XDMA驅動通常作為PCIe設備驅動實現,支持DMA傳輸、中斷處理等功能。

PCIe
AXI總線
Host CPU
XDMA引擎
FPGA邏輯

其實現DMA傳輸流程如下:

User Kernel DMA引擎 write()系統調用 配置源地址/目標地址 傳輸完成中斷 喚醒等待進程 User Kernel DMA引擎

3.2 XDMA驅動開發步驟

步驟1:定義PCI設備ID
#define PCI_VENDOR_ID_XILINX 0x10ee
#define PCI_DEVICE_ID_XDMA 0x7028static const struct pci_device_id xdma_pci_ids[] = {{ PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XDMA) },{ 0, }
};
MODULE_DEVICE_TABLE(pci, xdma_pci_ids);
步驟2:定義驅動主結構體
struct xdma_dev {struct pci_dev *pdev;void __iomem *bar[MAX_BARS];  // PCI BAR空間映射int irq;                     // 中斷號struct cdev cdev;            // 字符設備dev_t devno;                 // 設備號struct dma_chan *dma_chan;   // DMA通道// 其他設備特定數據...
};
步驟3:實現PCI probe函數

PCI設備探測流程:

Kernel PCIe設備 驅動 掃描PCI總線 返回Vendor/Device ID 調用probe()函數 Kernel PCIe設備 驅動

具體探測函數(probe)實現:

static int xdma_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{struct xdma_dev *xdev;int err, i;// 1. 分配設備結構體xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);if (!xdev)return -ENOMEM;xdev->pdev = pdev;pci_set_drvdata(pdev, xdev);// 2. 使能PCI設備err = pci_enable_device(pdev);if (err) {dev_err(&pdev->dev, "Failed to enable PCI device\n");goto fail;}// 3. 請求PCI資源err = pci_request_regions(pdev, "xdma");if (err) {dev_err(&pdev->dev, "Failed to request PCI regions\n");goto disable_device;}// 4. 映射BAR空間for (i = 0; i < MAX_BARS; i++) {if (!pci_resource_len(pdev, i))continue;xdev->bar[i] = pci_iomap(pdev, i, pci_resource_len(pdev, i));if (!xdev->bar[i]) {dev_err(&pdev->dev, "Failed to map BAR%d\n", i);err = -ENOMEM;goto release_regions;}}// 5. 設置DMA掩碼err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));if (err) {err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));if (err) {dev_err(&pdev->dev, "No suitable DMA available\n");goto unmap_bars;}}// 6. 申請中斷xdev->irq = pdev->irq;err = request_irq(xdev->irq, xdma_irq_handler, IRQF_SHARED, "xdma", xdev);if (err) {dev_err(&pdev->dev, "Failed to request IRQ\n");goto unmap_bars;}// 7. 初始化DMA引擎err = xdma_init_dma(xdev);if (err)goto free_irq;// 8. 注冊字符設備err = xdma_setup_cdev(xdev);if (err)goto deinit_dma;dev_info(&pdev->dev, "XDMA driver loaded successfully\n");return 0;// 錯誤處理...
}// 初始化DMA引擎
static int xdma_init_dma(struct xdma_dev *xdev)
{dma_cap_mask_t mask;dma_cap_zero(mask);dma_cap_set(DMA_MEMCPY, mask);xdev->dma_chan = dma_request_channel(mask, NULL, NULL);if (!xdev->dma_chan) {dev_err(&xdev->pdev->dev, "Failed to get DMA channel\n");return -ENODEV;}return 0;
}// 設置字符設備
static int xdma_setup_cdev(struct xdma_dev *xdev)
{int err;dev_t devno;err = alloc_chrdev_region(&devno, 0, 1, "xdma");if (err < 0) {dev_err(&xdev->pdev->dev, "Failed to allocate device number\n");return err;}xdev->devno = devno;cdev_init(&xdev->cdev, &xdma_fops);xdev->cdev.owner = THIS_MODULE;err = cdev_add(&xdev->cdev, devno, 1);if (err) {dev_err(&xdev->pdev->dev, "Failed to add cdev\n");unregister_chrdev_region(devno, 1);return err;}return 0;
}
步驟4:實現文件操作接口
static const struct file_operations xdma_fops = {.owner = THIS_MODULE,.open = xdma_open,.release = xdma_release,.read = xdma_read,.write = xdma_write,.unlocked_ioctl = xdma_ioctl,.llseek = no_llseek,
};static int xdma_open(struct inode *inode, struct file *filp)
{struct xdma_dev *xdev = container_of(inode->i_cdev, struct xdma_dev, cdev);filp->private_data = xdev;return 0;
}static int xdma_release(struct inode *inode, struct file *filp)
{filp->private_data = NULL;return 0;
}static ssize_t xdma_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{struct xdma_dev *xdev = filp->private_data;// 實現DMA讀取操作...return count;
}static ssize_t xdma_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{struct xdma_dev *xdev = filp->private_data;// 實現DMA寫入操作...return count;
}static long xdma_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{struct xdma_dev *xdev = filp->private_data;switch (cmd) {case XDMA_IOCTL_START_DMA:// 啟動DMA傳輸break;case XDMA_IOCTL_STOP_DMA:// 停止DMA傳輸break;case XDMA_IOCTL_GET_STATUS:// 獲取DMA狀態break;default:return -ENOTTY;}return 0;
}
步驟5:實現中斷處理
static irqreturn_t xdma_irq_handler(int irq, void *dev_id)
{struct xdma_dev *xdev = dev_id;u32 status;// 讀取中斷狀態寄存器status = ioread32(xdev->bar[0] + XDMA_IRQ_STATUS_REG);if (status & XDMA_IRQ_DONE) {// DMA傳輸完成中斷complete(&xdev->dma_complete);}if (status & XDMA_IRQ_ERROR) {// DMA錯誤中斷dev_err(&xdev->pdev->dev, "DMA error occurred\n");}// 清除中斷狀態iowrite32(status, xdev->bar[0] + XDMA_IRQ_STATUS_REG);return IRQ_HANDLED;
}
步驟6:實現DMA傳輸
static int xdma_do_transfer(struct xdma_dev *xdev, dma_addr_t src, dma_addr_t dst, size_t len)
{struct dma_async_tx_descriptor *tx;struct dma_device *dma_dev = xdev->dma_chan->device;enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;dma_cookie_t cookie;int err;// 準備DMA描述符tx = dma_dev->device_prep_dma_memcpy(xdev->dma_chan, dst, src, len, flags);if (!tx) {dev_err(&xdev->pdev->dev, "Failed to prepare DMA descriptor\n");return -EIO;}tx->callback = xdma_dma_callback;tx->callback_param = xdev;// 提交DMA傳輸cookie = dmaengine_submit(tx);err = dma_submit_error(cookie);if (err) {dev_err(&xdev->pdev->dev, "Failed to submit DMA transfer\n");return err;}// 觸發DMA傳輸dma_async_issue_pending(xdev->dma_chan);// 等待傳輸完成if (!wait_for_completion_timeout(&xdev->dma_complete, msecs_to_jiffies(1000))) {dev_err(&xdev->pdev->dev, "DMA transfer timeout\n");dmaengine_terminate_all(xdev->dma_chan);return -ETIMEDOUT;}return 0;
}static void xdma_dma_callback(void *data)
{struct xdma_dev *xdev = data;complete(&xdev->dma_complete);
}
步驟7:實現remove函數
static void xdma_remove(struct pci_dev *pdev)
{struct xdma_dev *xdev = pci_get_drvdata(pdev);int i;// 1. 移除字符設備cdev_del(&xdev->cdev);unregister_chrdev_region(xdev->devno, 1);// 2. 釋放DMA資源if (xdev->dma_chan)dma_release_channel(xdev->dma_chan);// 3. 釋放中斷free_irq(xdev->irq, xdev);// 4. 取消BAR空間映射for (i = 0; i < MAX_BARS; i++) {if (xdev->bar[i])pci_iounmap(pdev, xdev->bar[i]);}// 5. 釋放PCI資源pci_release_regions(pdev);// 6. 禁用PCI設備pci_disable_device(pdev);// 7. 釋放設備結構體devm_kfree(&pdev->dev, xdev);dev_info(&pdev->dev, "XDMA driver unloaded\n");
}
步驟8:定義PCI驅動結構體并注冊
static struct pci_driver xdma_driver = {.name = "xdma",.id_table = xdma_pci_ids,.probe = xdma_probe,.remove = xdma_remove,
};static int __init xdma_init(void)
{return pci_register_driver(&xdma_driver);
}static void __exit xdma_exit(void)
{pci_unregister_driver(&xdma_driver);
}module_init(xdma_init);
module_exit(xdma_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Xilinx XDMA Driver");

3.3 步驟總結

上文以xilinx XDMA 為例介紹了Linux PCI設備驅動開發步驟,總結成流程圖如下:

Yes
No
驅動模塊加載
module_init調用
PCI設備探測 probe
探測成功?
資源分配
映射BAR空間
申請中斷
初始化DMA引擎
注冊字符設備
錯誤處理
模塊退出
用戶空間操作
open/read/write
ioctl控制
mmap內存映射
DMA傳輸處理
中斷處理
數據傳輸完成
module_exit調用
釋放資源
注銷字符設備
釋放DMA資源
解除BAR映射
釋放中斷
禁用PCI設備

四、XDMA驅動測試與調試

4.1 加載驅動模塊

# 加載驅動
sudo insmod xdma.ko# 查看加載的模塊
lsmod | grep xdma# 查看內核日志
dmesg | tail

4.2 測試DMA傳輸

可以使用簡單的用戶空間程序測試DMA功能:

// test_xdma.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>#define XDMA_DEV "/dev/xdma"
#define BUF_SIZE (1024 * 1024)  // 1MBint main()
{int fd = open(XDMA_DEV, O_RDWR);if (fd < 0) {perror("Failed to open device");return -1;}// 分配測試緩沖區char *src = malloc(BUF_SIZE);char *dst = malloc(BUF_SIZE);if (!src || !dst) {perror("Failed to allocate buffers");close(fd);return -1;}// 填充源緩沖區memset(src, 0xAA, BUF_SIZE);memset(dst, 0, BUF_SIZE);// 寫入數據到設備ssize_t written = write(fd, src, BUF_SIZE);printf("Written %zd bytes to device\n", written);// 從設備讀取數據ssize_t readed = read(fd, dst, BUF_SIZE);printf("Read %zd bytes from device\n", readed);// 驗證數據if (memcmp(src, dst, BUF_SIZE) {printf("Data verification failed!\n");} else {printf("Data verification passed!\n");}free(src);free(dst);close(fd);return 0;
}

4.3 常見問題調試

  1. PCI設備未識別

    • 檢查lspci -nn確認設備ID是否正確
    • 確認內核配置中啟用了PCI支持
  2. DMA傳輸失敗

    • 檢查DMA掩碼設置
    • 確認物理地址是否正確
    • 檢查DMA引擎是否支持所需操作
  3. 中斷不觸發

    • 確認中斷號是否正確
    • 檢查中斷狀態寄存器
    • 確認中斷處理函數已正確注冊

五、性能優化技巧

5.1 使用分散/聚集DMA

static int xdma_sg_transfer(struct xdma_dev *xdev, struct scatterlist *sg_src,struct scatterlist *sg_dst,int sg_count)
{struct dma_async_tx_descriptor *tx;struct dma_device *dma_dev = xdev->dma_chan->device;enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;dma_cookie_t cookie;int err;tx = dma_dev->device_prep_dma_sg(xdev->dma_chan, sg_dst, sg_count,sg_src, sg_count,flags);if (!tx) {dev_err(&xdev->pdev->dev, "Failed to prepare SG DMA descriptor\n");return -EIO;}tx->callback = xdma_dma_callback;tx->callback_param = xdev;cookie = dmaengine_submit(tx);err = dma_submit_error(cookie);if (err) {dev_err(&xdev->pdev->dev, "Failed to submit SG DMA transfer\n");return err;}dma_async_issue_pending(xdev->dma_chan);if (!wait_for_completion_timeout(&xdev->dma_complete, msecs_to_jiffies(1000))) {dev_err(&xdev->pdev->dev, "SG DMA transfer timeout\n");dmaengine_terminate_all(xdev->dma_chan);return -ETIMEDOUT;}return 0;
}

5.2 實現零拷貝

static int xdma_mmap(struct file *filp, struct vm_area_struct *vma)
{struct xdma_dev *xdev = filp->private_data;unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;unsigned long size = vma->vm_end - vma->vm_start;int ret;// 將BAR空間映射到用戶空間if (offset >= pci_resource_len(xdev->pdev, 0) || size > pci_resource_len(xdev->pdev, 0) - offset) {return -EINVAL;}ret = remap_pfn_range(vma, vma->vm_start,(pci_resource_start(xdev->pdev, 0) + offset) >> PAGE_SHIFT,size, vma->vm_page_prot);if (ret)return -EAGAIN;return 0;
}

5.3 使用DMA池

// 初始化DMA池
xdev->dma_pool = dma_pool_create("xdma_pool", &xdev->pdev->dev,POOL_SIZE, POOL_ALIGN, 0);
if (!xdev->dma_pool) {dev_err(&xdev->pdev->dev, "Failed to create DMA pool\n");return -ENOMEM;
}// 從DMA池分配內存
void *buf = dma_pool_alloc(xdev->dma_pool, GFP_KERNEL, &dma_handle);
if (!buf) {dev_err(&xdev->pdev->dev, "Failed to allocate from DMA pool\n");return -ENOMEM;
}// 釋放DMA池內存
dma_pool_free(xdev->dma_pool, buf, dma_handle);// 銷毀DMA池
dma_pool_destroy(xdev->dma_pool);

六、總結

本文詳細介紹了Linux驅動框架和關鍵API,并以Xilinx XDMA驅動為例,展示了Linux驅動開發的完整流程。關鍵點包括:

  1. 理解Linux驅動框架:掌握字符設備、塊設備和網絡設備驅動的基本結構
  2. 熟悉關鍵API:模塊加載、文件操作、內存管理、中斷處理等核心API
  3. PCI驅動開發:從設備發現到資源管理的完整流程
  4. DMA傳輸實現:包括標準DMA和分散/聚集DMA
  5. 驅動調試技巧:日志分析、用戶空間測試程序等

通過XDMA驅動的實例,我們可以看到Linux驅動開發需要綜合考慮硬件特性、內核API和性能優化等多個方面。希望本文能為Linux驅動開發者提供有價值的參考。

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

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

相關文章

1. hadoop 集群的常用命令

1.上傳文件 1)hadoop fs -put words.txt /path/to/input/ 2)hdfs dfs -put words.txt /path/wc/input/ 2.獲取hdfs中的文件 hadoop fs -get /path/wc/input/words.txt 3.合并下載多個文件 hadoop fs -getmerge /path/wc/input/words.txt /path/wc/input/words2.txt 4.查…

Keepalived+LVS+nginx高可用架構

注明&#xff1a;所有軟件已經下載好&#xff0c;防火墻和SELinux已經全部關閉 一.搭建NFS 1.服務端 1.創建文件 [rootnfs ~]# mkdir -p /nfs/data 2、修改權限 [rootnfs ~]# chmod orw /nfs/data 3、寫配置文件 [rootnfs ~]# cat /etc/exports /nfs/data 192.168.111.118(r…

深度學習處理文本(13)

我們使用基于GRU的編碼器和解碼器來在Keras中實現這一方法。選擇GRU而不是LSTM&#xff0c;會讓事情變得簡單一些&#xff0c;因為GRU只有一個狀態向量&#xff0c;而LSTM有多個狀態向量。首先是編碼器&#xff0c;如代碼清單11-28所示。 代碼清單11-28 基于GRU的編碼器 fro…

HashMap 底層原理詳解

1. 核心數據結構 JDK 1.7 及之前&#xff1a;數組 鏈表 JDK 1.8 及之后&#xff1a;數組 鏈表/紅黑樹&#xff08;鏈表長度 ≥8 時轉紅黑樹&#xff0c;≤6 時退化為鏈表&#xff09; // JDK 1.8 的 Node 定義&#xff08;鏈表節點&#xff09; static class Node<K,V&g…

使用MySQL時出現 Ignoring query to other database 錯誤

Ignoring query to other database 錯誤 當在遠程連接軟件中輸入MySQL命令出現該錯誤 導致錯誤原因是&#xff1a;登錄mysql時賬戶名沒有加上u 如果出現該錯誤&#xff0c;退出mysql&#xff0c;重新輸入正確格式進入即可&#xff01;

哈爾濱工業大學:大模型時代的具身智能

大家好&#xff0c;我是櫻木。 機器人在工業領域&#xff0c;已經逐漸成熟。具身容易&#xff0c;智能難。 機器人-》智能機器人&#xff0c;需要自主能力&#xff0c;加上通用能力。 智能機器人-》人類&#xff0c;這個階段就太有想象空間了。而最受關注的-類人機器人。 如何…

Javascript代碼壓縮混淆工具terser詳解

原始的JavaScript代碼在正式的服務器上,如果沒有進行壓縮,混淆,不僅加載速度比較慢,而且還存在安全和性能問題. 因此現在需要進行壓縮,混淆處理. 處理方案簡單描述一下: 1. 使用 terser 工具進行 安裝 terser工具: # npm 安裝 npm install terser --save-dev# 或使用 yarn 安…

Java String 常用方法詳解

目錄 一、獲取字符串信息(一)獲取字符串長度(二)獲取指定索引處的字符(三)獲取子字符串二、字符串比較(一)比較字符串內容(二)忽略大小寫比較三、字符串轉換(一)轉換為大寫(二)轉換為小寫四、字符串查找(一)查找子字符串的位置(二)從指定位置開始查找五、字符…

Linux驅動開發練習案例

1 開發目標 1.1 架構圖 操作系統&#xff1a;基于Linux5.10.10源碼和STM32MP157開發板&#xff0c;完成tf-a(FSBL)、u-boot(SSBL)、uImage、dtbs的裁剪&#xff1b; 驅動層&#xff1a;為每個外設配置DTS并且單獨封裝外設驅動模塊。其中電壓ADC測試&#xff0c;采用linux內核…

leetcode-代碼隨想錄-哈希表-贖金信

題目 題目鏈接&#xff1a;383. 贖金信 - 力扣&#xff08;LeetCode&#xff09; 給你兩個字符串&#xff1a;ransomNote 和 magazine &#xff0c;判斷 ransomNote 能不能由 magazine 里面的字符構成。 如果可以&#xff0c;返回 true &#xff1b;否則返回 false 。 maga…

精品可編輯PPT | “新基建”在數字化智慧高速公路中的支撐應用方案智慧建筑智慧交通解決方案施工行業解決方案

本文詳細闡述了“新基建”在數字化智慧高速公路中的支撐應用方案&#xff0c;從政策背景出發&#xff0c;指出國家在交通領域的一系列發展規劃和指導意見&#xff0c;強調了智慧交通建設的重要性。分析了當前高速公路存在的問題&#xff0c;如基礎感知設施不足、協同水平低、服…

C語言求3到100之間的素數

一、代碼展示 二、運行結果 三、感悟思考 注意: 這個題思路他是一個試除法的一個思路 先進入一個for循環 遍歷3到100之間的數字 第二個for循環則是 判斷他不是素數 那么就直接退出 這里用break 是素數就打印出來 在第一個for循環內 第二個for循環外

英語—四級CET4考試—蒙猜篇—匹配題

蒙猜方法一 匹配題的做題&#xff1a; 方法一&#xff1a; 首先&#xff0c;什么都不想&#xff0c;把問題中ing形式的&#xff0c;大寫字母的&#xff0c;人名&#xff0c;地名&#xff0c;最后幾個依次框起來。 然后&#xff0c;比如46題&#xff0c;口里默念meaningful lif…

股票日數據使用_未復權日數據生成前復權日周月季年數據

目錄 前置&#xff1a; 準備 代碼&#xff1a;數據庫交互部分 代碼&#xff1a;生成前復權 日、周、月、季、年數據 前置&#xff1a; 1 未復權日數據獲取&#xff0c;請查看 https://blog.csdn.net/m0_37967652/article/details/146435589 數據庫使用PostgreSQL。更新日…

系統與網絡安全------Windows系統安全(6)

資料整理于網絡資料、書本資料、AI&#xff0c;僅供個人學習參考。 共享文件夾 發布共享文件夾 Windows共享概述 微軟公司推出的網絡文件/打印機服務系統 可以將一臺主機的資源發布給其他主機共有 共享訪問的優點 方便、快捷相比光盤 U盤不易受文件大小限制 可以實現訪問…

BN 層的作用, 為什么有這個作用?

BN 層&#xff08;Batch Normalization&#xff09;——這是深度神經網絡中非常重要的一環&#xff0c;它大大改善了網絡的訓練速度、穩定性和收斂效果。 &#x1f9e0; 一句話理解 BN 層的作用&#xff1a; Batch Normalization&#xff08;批歸一化&#xff09;通過標準化每一…

判斷HiveQL語句為ALTER TABLE語句的識別函數

寫一個C#字符串解析程序代碼&#xff0c;邏輯是從前到后一個一個讀取字符&#xff0c;遇到匹配空格、Tab和換行符就繼續讀取下一個字符&#xff0c;遇到大寫或小寫的字符a&#xff0c;就讀取后一個字符并匹配是否為大寫或小寫的字符l&#xff0c;以此類推&#xff0c;匹配任意字…

基于編程的運輸設備管理系統設計(vue+springboot+ssm+mysql8.x)

基于編程的運輸設備管理系統設計&#xff08;vuespringbootssmmysql8.x&#xff09; 運輸設備信息管理系統是一個全面的設備管理平臺&#xff0c;旨在優化設備管理流程&#xff0c;提高運輸效率。系統提供登錄入口&#xff0c;確保只有授權用戶可以訪問。個人中心讓用戶可以查…

6.1 python加載win32或者C#的dll的方法

python很方便的可以加載win32的方法以及C#編寫的dll中的方法或者變量&#xff0c;大致過程如下。 一.python加載win32的方法&#xff0c;使用win32api 1.安裝庫win32api pip install win32api 2.加載所需的win32函數并且調用 import win32api win32api.MessageBox(0,"…

前端精度計算:Decimal.js 基本用法與詳解

一、Decimal.js 簡介 decimal.js 是一個用于任意精度算術運算的 JavaScript 庫&#xff0c;它可以完美解決浮點數計算中的精度丟失問題。 官方API文檔&#xff1a;Decimal.js 特性&#xff1a; 任意精度計算&#xff1a;支持大數、小數的高精度運算。 鏈式調用&#xff1a;…