主頁 > 後端開發 > STM32F103 串口DMA + 空閑中斷 實作不定長資料收發

STM32F103 串口DMA + 空閑中斷 實作不定長資料收發

2020-11-16 15:03:00 後端開發

串口DMA系列文章一共兩篇,分別是利用DMA + 空閑中斷實作不定長資料的收發,另一篇是使用DMA中斷實作定長資料的收發,文章鏈接如下:
01 STM32F103 串口DMA + 空閑中斷 實作不定長資料收發
02 STM32F103 串口 +DMA中斷實作資料收發

文章目錄

  • 1. 空閑中斷
    • 1.1 uart_dma.c
    • 1.2 uart_dma.h
    • 1.3 main.c
    • 1.4 stm32f10x_it.c
    • 1.5 效果演示
    • 1.6 知識補充
      • 1.6.1 外設的基地址
      • 1.6.2 空閑中斷清除
      • 1.6.3 DMA傳輸數量

DMA(Direct Memory Access,直接存盤器訪問) 是所有現代電腦的重要特色,它允許不同速度的硬體裝置來溝通,而不需要依賴于 CPU 的大量中斷負載,否則,CPU 需要從來源把每一片段的資料復制到暫存器,然后把它們再次寫回到新的地方,在這個時間中,CPU 對于其他的作業來說就無法使用,
關于DMA的基礎知識請參考文章 https://blog.csdn.net/gdjason/article/details/51019219 ,個人覺得博主的文章最后代碼部分寫的有點亂,再次做整理記錄,串口DMA可以有兩種中斷觸發方式,一種是使用STM32的 IDLE 空閑中斷方便接收不定長的資料,使用中也經常采用這種方式,第二種是使用DMA自身的傳輸完成中斷,這兩種方式在發送完成后均能產生發送完成中斷,不同的地方是空閑中斷方便接收不定長的資料,而DMA傳輸完成中斷只有接收到定義好的長度的資料后才會產生接收中斷,

1. 空閑中斷

本文采用485總線來實驗串口的DMA空閑中斷,實作資料的收發測驗,485的不同之處是多了一個使能引腳,該引腳是高電平使能發送,低電平使能接收,因此是半雙工的通信,其他的和串口一致,本例使用STM32F103的串口1,TX引腳是PA9;RX引腳是PA10,485的使能引腳是PD1,大家在使用時板子上的串口功能測驗時無需考慮485的使能引腳,本文中關于485的部分都用 /**** RS485 ****/標識出來,下面是代碼部分,

1.1 uart_dma.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_dma.h"
#include "misc.h"

#include "systick.h"	// 利用嘀嗒計時器實作了ms級的死等延時,用于切換485收發功能使用,實際專案中不能用死等延時
#include "uart_dma.h"

uint8_t uart1RecvData[32] = {0};    // 接收資料緩沖區
uint8_t uart1RecvFlag = 0;          // 接收完成標志位
uint8_t uart1RecvLen = 0;           // 接收的資料長度

uint8_t uart1SendData[32] = {0};    // 發送資料緩沖區
uint8_t uart1SendFlag = 0;          // 發送完成標志位

/* 串口1 GPIO引腳初始化 */
void Uart1GpioInit(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   // 使能GPIOA時鐘

	/************ ↓ RS485 相關 ↓ ************/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);   // 使能GPIOD時鐘
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;    // 輸入輸出使能引腳 推挽輸出
    GPIO_InitStruct.GPIO_Pin = UART1_EN_PIN;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(UART1_EN_PORT, &GPIO_InitStruct);     // PD1
    Uart1RxEnable();    // 初始化接收模式
    /************ ↑ RS485 相關 ↑ ************/
    
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;    // TX 推挽輸出
    GPIO_InitStruct.GPIO_Pin = UART1_TX_PIN;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(UART1_TX_PORT, &GPIO_InitStruct);     // PA9
    
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;      // RX上拉輸入
    GPIO_InitStruct.GPIO_Pin = UART1_RX_PIN;
    GPIO_Init(UART1_RX_PORT, &GPIO_InitStruct);     // PA10
}

/************ ↓ RS485 相關 ↓ ************/
/* 使能485發送 */
void Uart1TxEnable(void)
{
    GPIO_WriteBit(UART1_EN_PORT, UART1_EN_PIN, Bit_SET);    // 485的使能引腳,高電平為使能發送
    Delay_ms(5);
}

/* 使能485接收 */
void Uart1RxEnable(void)
{
    GPIO_WriteBit(UART1_EN_PORT, UART1_EN_PIN, Bit_RESET);  // 485的使能引腳,低電平為使能發送
    Delay_ms(5);
}
/************ ↑ RS485 相關 ↑ ************/
 
/* 串口1配置 9600 8n1 */
void Uart1Config(void)
{
    USART_InitTypeDef USART_InitStruct;		// 串口配置
    NVIC_InitTypeDef NVIC_InitStructure;	// 中斷配置
    DMA_InitTypeDef DMA_InitStruct;			// DMA 配置
    
    USART_DeInit(USART1);   // 暫存器恢復默認值
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);  // 使能串口時鐘
    
    /* 串口引數配置 */
    USART_InitStruct.USART_BaudRate = BAUD_RATE;            // 波特率:9600
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;    // 無流控
    USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;    // 收發
    USART_InitStruct.USART_Parity = USART_Parity_No;                // 無校驗位 
    USART_InitStruct.USART_StopBits = USART_StopBits_1;             // 1個停止位
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;        // 8個資料位
    USART_Init(USART1, &USART_InitStruct);
    USART_Cmd(USART1, ENABLE);  // 使能串口
    
    /* 串口中斷配置 */
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             // 使能
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;   // 搶占優先級
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;          // 子優先級
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;           // 串口1中斷
    NVIC_Init(&NVIC_InitStructure);     // 嵌套向量中斷控制器初始化

    USART_ITConfig(USART1, USART_IT_TC,   ENABLE);  // 使能串口發送中斷,發送完成產生 USART_IT_TC 中斷
    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);  // 使能串口空閑中斷,接收一幀資料產生 USART_IT_IDLE 空閑中斷
    
    /* 串口DMA配置 */
    DMA_DeInit(DMA1_Channel4);  // DMA1 通道4,暫存器復位
    DMA_DeInit(DMA1_Channel5);  // DMA1 通道5,暫存器復位
    
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);  // 使能 DMA1 時鐘
    
    // RX DMA1 通道5
    DMA_InitStruct.DMA_BufferSize = sizeof(uart1RecvData);      // 定義了接收的最大長度
    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;             // 串口接收,方向是外設->記憶體
    DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;                   // 本次是外設到記憶體,所以關閉記憶體到記憶體
    DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)uart1RecvData;// 記憶體的基地址,要存盤在哪里
    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;// 記憶體資料寬度,按照位元組存盤
    DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;        // 記憶體遞增,每次串口收到資料存在記憶體中,下次收到自動存盤在記憶體的下一個位置
    DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;                  // 正常模式
    DMA_InitStruct.DMA_PeripheralBaseAddr = USART1_BASE + 0x04; // 外設的基地址,串口的資料暫存器
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;    // 外設的資料寬度,按照位元組存盤,與記憶體的資料寬度一致
    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;   // 接收只有一個資料暫存器 RDR,所以外設地址不遞增
    DMA_InitStruct.DMA_Priority = DMA_Priority_High;            // 優先級
    DMA_Init(DMA1_Channel5, &DMA_InitStruct);
    
    // TX DMA1 通道4  
    DMA_InitStruct.DMA_BufferSize = 0;                          // 發送緩沖區的大小,初始化為0不發送
    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;             // 發送是方向是外設到記憶體,外設作為目的地
    DMA_InitStruct.DMA_MemoryBaseAddr =(uint32_t)uart1SendData; // 發送記憶體地址,從哪里發送
    DMA_Init(DMA1_Channel4, &DMA_InitStruct);
     
    USART_DMACmd(USART1, USART_DMAReq_Tx | USART_DMAReq_Rx, ENABLE);// 使能DMA串口發送和接受請求
    DMA_Cmd(DMA1_Channel5, ENABLE);     // 使能接收
    DMA_Cmd(DMA1_Channel4, DISABLE);    // 禁止發送
}

/* 清除DMA的傳輸數量暫存器 */
void uart1DmaClear(void)
{
    DMA_Cmd(DMA1_Channel5, DISABLE);    // 關閉 DMA1_Channel5 通道
    DMA_SetCurrDataCounter(DMA1_Channel5, sizeof(uart1RecvData));   // 重新寫入要傳輸的資料數量
    DMA_Cmd(DMA1_Channel5, ENABLE);     // 使能 DMA1_Channel5 通道
}

/* 串口1發送陣列 */
void uart1SendArray(uint8_t *arr, uint8_t len)
{
    if(len == 0)	// 判斷長度是否有效
      return;
	
	uint8_t sendLen = len>sizeof(uart1SendData) ? sizeof(uart1SendData) : len;	// 防止越界

    /************ ↓ RS485 相關 ↓ ************/ 
    Uart1TxEnable();    // 使能發送
    /************ ↑ RS485 相關 ↑ ************/
    
    while (DMA_GetCurrDataCounter(DMA1_Channel4));  // 檢查DMA發送通道內是否還有資料
    if(arr) 
      memcpy(uart1SendData, arr, sendLen);
    
    // DMA發送資料-要先關 設定發送長度 開啟DMA
    DMA_Cmd(DMA1_Channel4, DISABLE);
    DMA_SetCurrDataCounter(DMA1_Channel4, sendLen);   // 重新寫入要傳輸的資料數量
    DMA_Cmd(DMA1_Channel4, ENABLE);     // 啟動DMA發送  
}

1.2 uart_dma.h

#ifndef _UART_DAM_H_
#define _UART_DMA_H_

#include <stdint.h>

#define UART1_TX_PORT   GPIOA
#define UART1_TX_PIN    GPIO_Pin_9
#define UART1_RX_PORT   GPIOA
#define UART1_RX_PIN    GPIO_Pin_10
#define UART1_EN_PORT   GPIOD
#define UART1_EN_PIN    GPIO_Pin_1
#define BAUD_RATE       (9600)

extern uint8_t uart1RecvData[32];
extern uint8_t uart1RecvFlag;
extern uint8_t uart1RecvLen;
extern uint8_t uart1SendFlag;

void Uart1GpioInit(void);
void Uart1Config(void);
void uart1DmaClear(void);
void uart1SendArray(uint8_t *arr, uint8_t len);

/************ ↓ RS485 相關 ↓ ************/ 
void Uart1RxEnable(void);
void Uart1TxEnable(void);
/************ ↑ RS485 相關 ↑ ************/

#endif  /* uart_dma.h */

1.3 main.c

#include "uart_dma.h"
#include "misc.h"

int main()
{ 
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  // 設定中斷優先級分組
    
    /************ ↓ RS485 相關 ↓ ************/ 
    SysTickInit();          // 嘀嗒計時器初始化,沒用485可以省去
    /************ ↑ RS485 相關 ↑ ************/
    
    Uart1GpioInit();	// 串口GPIO初始化
    Uart1Config();		// 串口和DMA配置

    while(1)
    {     
        if(uart1RecvFlag == 1)	// 接收到資料
        {
            uart1RecvFlag = 0;  // 接收標志清空
            uart1DmaClear();    // 清空DMA接收通道
            uart1SendArray(uart1RecvData, uart1RecvLen);        // 使用DMA發送資料
            memset(uart1RecvData, '\0', sizeof(uart1RecvData)); // 清空接識訓沖區
        }
        
        if(uart1SendFlag == 1)
        {
            uart1SendFlag = 0;  // 清空發送標志   
            Uart1RxEnable();    // 發送完成打開接收
        }
    }
}

1.4 stm32f10x_it.c

#include "stm32f10x_it.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_dma.h"

#include "uart_dma.h"

void USART1_IRQHandler(void)    // 串口1 的中斷處理函式
{
    uint8_t clear;

    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)   // 空閑中斷
    {
        clear = USART1->SR; // 清除空閑中斷
        clear = USART1->DR; // 清除空閑中斷
        
        uart1RecvFlag = 1;  // 置接收標志位
        uart1RecvLen = sizeof(uart1RecvData) - DMA_GetCurrDataCounter(DMA1_Channel5);// 總的buf長度減去剩余buf長度,得到接收到資料的長度
    }   
    
    if(USART_GetITStatus(USART1, USART_IT_TC) != RESET)     // 發送完成
    {
        USART_ClearITPendingBit(USART1, USART_IT_TC);       // 清除完成標記
        DMA_Cmd(DMA1_Channel4, DISABLE);                    // 關閉DMA
        uart1SendFlag = 1;                                  // 設定發送完成標志位
    }
}

1.5 效果演示

本文使用的是串口采用DMA空閑中斷接收不定長的資料,收到后將收到的資料使用DMA發送出去,采用串口除錯助手進行除錯,效果如下:可以看到當資料超過定義的最大資料(本例是32個位元組)后,接收只能接收到32個資料,其他的資料將被丟棄,
在這里插入圖片描述

1.6 知識補充

1.6.1 外設的基地址

本文外設到基地址定義的是 USART1_BASE + 0x04,為什么是這個呢?查看STM32的參考手冊,找到串口章節,查找USART暫存器地址映象如下圖:可以看到資料暫存器是串口基地址暫存器偏移 0x04 后的位置,串口1基地址的宏定義可以在 stm32f10x.h 中找到,如下圖,
在這里插入圖片描述
串口1基地址宏定義

1.6.2 空閑中斷清除

產生空閑中斷后,先讀SR,再讀DR可以清除空閑中斷標志位,見下圖(手冊串口章節 25.6.1 狀態暫存器(USART_SR)),
在這里插入圖片描述

1.6.3 DMA傳輸數量

查看手冊,DMA傳輸數量暫存器里的值表示剩余待傳輸的位元組數,因此在 it.c 中使用 定義的總數-暫存器中的數值=表示收到的個數,這個地方需要特別注意,否則會計算錯收到的位元組數,
在這里插入圖片描述
還有一點,每次接收完成之后為了使下次的接收是從記憶體中的下標0還是存盤,需要重新寫入要傳輸的資料數量,否則下次直接接著上次傳輸的位置開始接收存盤,發送時也一樣,見代碼 uart_dma.c 中的127–129行和149–151行,
如果注釋掉128行,每次不重新寫入傳輸數量暫存器,演示結果如下:從結果可以看出如果注釋掉128行,那么DMA往接識訓沖區搬運資料時下標將從上次結束的位置作為起始位置【因為此時傳輸數量暫存器的值不是0,是32-7】,前面是’/0’ 是因為每次接收完成后都將接識訓沖區清空,所以每次接收一幀資料之后都要將 傳輸數量暫存器清0,
在這里插入圖片描述

在DMA學習程序中有什么問題,歡迎一起交流,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/219929.html

標籤:python

上一篇:嵌入式系統原理課后習題練習

下一篇:【涂鴉物聯網足跡】涂鴉云平臺介面串列—萬能紅外遙控器

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more