开发手机网站用什么好,怎么查询商标名称是否注册,球形网架结构厂家,漯河公司做网站看了网上的很多的SPIDMA的代码#xff0c;感觉都有一些缺陷#xff0c;就是基本都是需要有手动等待DMA完成的这个操作#xff0c;我感觉这种等待操作在很大程度上浪费了时间#xff0c;那么我加入的“队列”就是一种将等待时间利用起来的方法。原本的SPIDMA的操作逻辑如下图…看了网上的很多的SPIDMA的代码感觉都有一些缺陷就是基本都是需要有手动等待DMA完成的这个操作我感觉这种等待操作在很大程度上浪费了时间那么我加入的“队列”就是一种将等待时间利用起来的方法。原本的SPIDMA的操作逻辑如下图是比较简单的下面是我加入队列逻辑的过程图会变得比较庞大占用的内容资源也多会比较多需要斟酌使用加入队列前的基本流程是控制DC电平-写入数据-等待DMA传输完成-DC电平-写入数据-等待DMA传输完成这种操作是很浪费时间资源的那么加入队列之后的操作是写入数据-写入数据-写入数据大部分时间都在写入队列与中断中其它部分都是DMA自己在传输数据不需要一直等待我会在最后放一个完整的代码我使用的是ST7789其中的执行代码是适配LVGL的包括我自己使用芯片的驱动有需要的可以直接拿走基本上只需要修改一点点就可以用了。首先是队列的创建我这里只是简单写了一下这个队列创建很占内存内存分配也不是很灵活其中忙标志也没有锁小概率会出事的如果有大佬的话可以修改一下使其更完善一点
#define SPI_BUFFER_SIZE 4096
#define SPI_QUEUE_SIZE 8//数据结构体
typedef struct {uint8_t data[SPI_BUFFER_SIZE];uint16_t data_size;bool is_data; // 0:命令, 1:数据 用于控制DC
} spi_transaction_t;// 全局传输队列
spi_transaction_t tx_queue[SPI_QUEUE_SIZE];//队列索引
volatile uint16_t tx_read_index 0;
volatile uint16_t tx_write_index 0;
volatile uint16_t tx_count 0;
//DMA忙标志
volatile uint8_t dma_busy 0;之后是队列数据的输入需要输入数据地址数据大小还有DC电平翻转方向0命令/1数据)如果是ESP32的话好像可以实现DC的自动翻转会方便很多这里实现不了就只能在DMA之前先把DC翻转给提前完成 if (tx-is_data)。还要注意队列的索引变化
/******************************************************************************函数说明添加SPI传输任务到队列
******************************************************************************/
static void LCD_Add_To_Queue(uint8_t *data, uint16_t size, uint8_t is_data)
{// 等待队列有空位非阻塞式可添加超时机制while (tx_count SPI_QUEUE_SIZE) {// 可在此处添加少量延迟或任务调度}// 复制数据到队列uint8_t index tx_write_index;memcpy(tx_queue[index].data, data, size);tx_queue[index].data_size size;tx_queue[index].is_data is_data;// 更新队列索引tx_write_index (tx_write_index 1) % SPI_QUEUE_SIZE;tx_count;// 如果DMA空闲立即启动传输if (!dma_busy) {LCD_Start_Next_Transaction();}
}/******************************************************************************函数说明启动下一个DMA传输任务
******************************************************************************/
static void LCD_Start_Next_Transaction(void)
{if (tx_count 0){ //lv_disp_t *disp lv_disp_get_default();//if (disp ! NULL) {// lv_disp_flush_ready(disp-driver); //}这个是为了适配LVGL加的return;}else if(dma_busy){return;}uint8_t index tx_read_index;spi_transaction_t *tx tx_queue[index];// 设置DC引脚if (tx-is_data) {OLED_DC_Set(); // 数据模式} else {OLED_DC_Clr(); // 命令模式}OLED_CS_Clr();dma_busy 1;HAL_SPI_Transmit_DMA(hspi1, tx-data, tx-data_size);
}然后最后一个是DMA传输完成的回调进入回调后需要寻找队列中是否还有数据如果还有数据就把数据传给DMA
/******************************************************************************函数说明DMA传输完成回调
******************************************************************************/
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{if (hspi hspi1) {OLED_CS_Set(); // 传输完成拉高CSdma_busy 0;// 更新队列tx_read_index (tx_read_index 1) % SPI_QUEUE_SIZE;tx_count--;// 立即启动下一个传输if (tx_count 0){ //lv_disp_t *disp lv_disp_get_default();//if (disp ! NULL) {// lv_disp_flush_ready(disp-driver);//}这个是为了适配LVGL}else if (tx_count 0) {LCD_Start_Next_Transaction();}}
}最后是完整版本的代码
#include oled.h
#include stdbool.h
#include stdint.h
//#include lvgl.h
#include stm32f4xx_hal.h
// 在oled.h中定义#define SPI_BUFFER_SIZE 4096#define SPI_QUEUE_SIZE 8
typedef struct {uint8_t data[SPI_BUFFER_SIZE];uint16_t data_size;bool is_data; // 0:命令, 1:数据
} spi_transaction_t;// 全局传输队列
spi_transaction_t tx_queue[SPI_QUEUE_SIZE];//队列索引
volatile uint16_t tx_read_index 0;
volatile uint16_t tx_write_index 0;
volatile uint16_t tx_count 0;
//DMA忙标志
volatile uint8_t dma_busy 0;// 外部 SPI 句柄
extern SPI_HandleTypeDef hspi1;/******************************************************************************函数说明启动下一个DMA传输任务
******************************************************************************/
static void LCD_Start_Next_Transaction(void)
{if (tx_count 0){ //lv_disp_t *disp lv_disp_get_default();//if (disp ! NULL) {// lv_disp_flush_ready(disp-driver); // 正确//}return;}else if(dma_busy){return;}uint8_t index tx_read_index;spi_transaction_t *tx tx_queue[index];// 设置DC引脚if (tx-is_data) {OLED_DC_Set(); // 数据模式} else {OLED_DC_Clr(); // 命令模式}OLED_CS_Clr();dma_busy 1;HAL_SPI_Transmit_DMA(hspi1, tx-data, tx-data_size);
}/******************************************************************************函数说明添加SPI传输任务到队列
******************************************************************************/
static void LCD_Add_To_Queue(uint8_t *data, uint16_t size, uint8_t is_data)
{// 等待队列有空位非阻塞式可添加超时机制while (tx_count SPI_QUEUE_SIZE) {// 可在此处添加少量延迟或任务调度}// 复制数据到队列uint8_t index tx_write_index;memcpy(tx_queue[index].data, data, size);tx_queue[index].data_size size;tx_queue[index].is_data is_data;// 更新队列索引tx_write_index (tx_write_index 1) % SPI_QUEUE_SIZE;tx_count;// 如果DMA空闲立即启动传输if (!dma_busy) {LCD_Start_Next_Transaction();}
}/******************************************************************************函数说明DMA传输完成回调
******************************************************************************/
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{if (hspi hspi1) {OLED_CS_Set(); // 传输完成拉高CSdma_busy 0;// 更新队列tx_read_index (tx_read_index 1) % SPI_QUEUE_SIZE;tx_count--;// 立即启动下一个传输if (tx_count 0){ //lv_disp_t *disp lv_disp_get_default();//if (disp ! NULL) {// lv_disp_flush_ready(disp-driver);//}}else if (tx_count 0) {LCD_Start_Next_Transaction();}}
}/******************************************************************************函数说明DMA传输错误回调
******************************************************************************/
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{if (hspi hspi1) {//OLED_CS_Set(); // 错误时也要拉高CSdma_busy 0;// 可添加错误处理逻辑}
}/******************************************************************************函数说明LCD写命令
******************************************************************************/
void LCD_WR_REG(u8 dat)
{LCD_Add_To_Queue(dat, 1, 0); // 0表示命令
}/******************************************************************************函数说明LCD写8位数据
******************************************************************************/
void LCD_WR_DATA8(u8 dat)
{LCD_Add_To_Queue(dat, 1, 1); // 1表示数据
}/******************************************************************************函数说明LCD写16位数据
******************************************************************************/
void LCD_WR_DATA(u16 dat)
{uint8_t temp[2] {dat 8, dat 0xFF};LCD_Add_To_Queue(temp, 2, 1); // 1表示数据
}/******************************************************************************函数说明批量写入数据优化性能
******************************************************************************/
void LCD_Write_Bulk(uint8_t *data, uint16_t size)
{LCD_Add_To_Queue(data, size, 1);
}/******************************************************************************函数说明设置显示区域起始坐标和结束坐标参数说明x1,x2 起始和结束的列地址y1,y2 起始和结束的行地址返回值 无
******************************************************************************/
void LCD_Address_Set(u16 x1, u16 y1, u16 x2, u16 y2)
{if (USE_HORIZONTAL 0){LCD_WR_REG(0x2A); // Column Address SetLCD_WR_DATA(x1);LCD_WR_DATA(x2);LCD_WR_REG(0x2B); // Page Address SetLCD_WR_DATA(y1);LCD_WR_DATA(y2);LCD_WR_REG(0x2C); // Memory Write}else if (USE_HORIZONTAL 1){LCD_WR_REG(0x2A);LCD_WR_DATA(y1); // 注意x 和 y 互换LCD_WR_DATA(y2);LCD_WR_REG(0x2B);LCD_WR_DATA(239 - x2); // 坐标翻转LCD_WR_DATA(239 - x1);LCD_WR_REG(0x2C);}else if (USE_HORIZONTAL 2){LCD_WR_REG(0x2A);LCD_WR_DATA(239 - x2);LCD_WR_DATA(239 - x1);LCD_WR_REG(0x2B);LCD_WR_DATA(319 - y2);LCD_WR_DATA(319 - y1);LCD_WR_REG(0x2C);}else if (USE_HORIZONTAL 3){LCD_WR_REG(0x2A);LCD_WR_DATA(319 - y2);LCD_WR_DATA(319 - y1);LCD_WR_REG(0x2B);LCD_WR_DATA(x1);LCD_WR_DATA(x2);LCD_WR_REG(0x2C);}
}/******************************************************************************函数说明LCD初始化参数说明无返回值 无
******************************************************************************/
void Lcd_Init(void)
{OLED_RES_Clr();HAL_Delay(200);OLED_RES_Set();HAL_Delay(100);//************* Start Initial Sequence **********//LCD_WR_REG(0x36);if (USE_HORIZONTAL 0) LCD_WR_DATA8(0x00);else if (USE_HORIZONTAL 1) LCD_WR_DATA8(0xC0);else if (USE_HORIZONTAL 2) LCD_WR_DATA8(0x70);else LCD_WR_DATA8(0xA0);LCD_WR_REG(0x3A);LCD_WR_DATA8(0x05);LCD_WR_REG(0xB2);LCD_WR_DATA8(0x0C);LCD_WR_DATA8(0x0C);LCD_WR_DATA8(0x00);LCD_WR_DATA8(0x33);LCD_WR_DATA8(0x33);LCD_WR_REG(0xB7);LCD_WR_DATA8(0x35);LCD_WR_REG(0xBB);LCD_WR_DATA8(0x19);LCD_WR_REG(0xC0);LCD_WR_DATA8(0x2C);LCD_WR_REG(0xC2);LCD_WR_DATA8(0x01);LCD_WR_REG(0xC3);LCD_WR_DATA8(0x12);LCD_WR_REG(0xC4);LCD_WR_DATA8(0x20);LCD_WR_REG(0xC6);LCD_WR_DATA8(0x0F);LCD_WR_REG(0xD0);LCD_WR_DATA8(0xA4);LCD_WR_DATA8(0xA1);LCD_WR_REG(0xE0);LCD_WR_DATA8(0xD0);LCD_WR_DATA8(0x04);LCD_WR_DATA8(0x0D);LCD_WR_DATA8(0x11);LCD_WR_DATA8(0x13);LCD_WR_DATA8(0x2B);LCD_WR_DATA8(0x3F);LCD_WR_DATA8(0x54);LCD_WR_DATA8(0x4C);LCD_WR_DATA8(0x18);LCD_WR_DATA8(0x0D);LCD_WR_DATA8(0x0B);LCD_WR_DATA8(0x1F);LCD_WR_DATA8(0x23);LCD_WR_REG(0xE1);LCD_WR_DATA8(0xD0);LCD_WR_DATA8(0x04);LCD_WR_DATA8(0x0C);LCD_WR_DATA8(0x11);LCD_WR_DATA8(0x13);LCD_WR_DATA8(0x2C);LCD_WR_DATA8(0x3F);LCD_WR_DATA8(0x44);LCD_WR_DATA8(0x51);LCD_WR_DATA8(0x2F);LCD_WR_DATA8(0x1F);LCD_WR_DATA8(0x1F);LCD_WR_DATA8(0x20);LCD_WR_DATA8(0x23);LCD_WR_REG(0x21); // 逆显示LCD_WR_REG(0x11); // Sleep OutHAL_Delay(120);LCD_WR_REG(0x29); // Display On
}/******************************************************************************函数说明填充指定区域参数说明xsta,ysta 起始坐标xend,yend 结束坐标返回值 无
******************************************************************************/
void LCD_Fill(u16 xsta, u16 ysta, u16 xend, u16 yend, u16 *color) {LCD_Address_Set(xsta, ysta, xend, yend);uint32_t total_pixels (xend - xsta 1) * (yend - ysta 1);uint8_t *color_byte (uint8_t*)color; // 转换为字节指针uint32_t transferred_pixels 0;while (transferred_pixels total_pixels) {// 每次传输最大块SPI_BUFFER_SIZE字节 缓冲区容量uint32_t chunk_bytes SPI_BUFFER_SIZE;// 剩余像素对应的字节数 (总像素 - 已传像素) * 2uint32_t remaining_bytes (total_pixels - transferred_pixels) * 2;if (chunk_bytes remaining_bytes) {chunk_bytes remaining_bytes;}// 入队当前块LCD_Write_Bulk(color_byte (transferred_pixels * 2), chunk_bytes);transferred_pixels chunk_bytes / 2; // 字节数转像素数}
}// 临时缓冲区10 行
static u16 fill_buf[LCD_H * 10];/*** brief 测试逐块填充整个屏幕为蓝色*/
void test_fill_entire_screen_blue(void)
{uint16_t y;HAL_Delay(500);// 填充当前块为蓝色for (int i 0; i LCD_H * 10; i) {fill_buf[i] YELLOW;}// 从 Y0 开始每次填充 10 行直到填满 240 行for (y 0; y LCD_H; y 10) {uint16_t y_end (y 9) LCD_H ? (y 9) : (LCD_H - 1);LCD_Fill(0, y, LCD_W, y_end, fill_buf);}
}还有对应的.h文件
#ifndef __OLED_H
#define __OLED_H #include main.h
#include stdlib.h
#include string.h#define USE_HORIZONTAL 0#if USE_HORIZONTAL 0 || USE_HORIZONTAL 2#define LCD_W 240#define LCD_H 320
#else#define LCD_W 320#define LCD_H 240
#endif#define u8 unsigned char
#define u16 unsigned int
#define u32 unsigned long#define OLED_CS_Clr() HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_RESET)
#define OLED_CS_Set() HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, GPIO_PIN_SET)#define OLED_DC_Clr() HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_RESET)
#define OLED_DC_Set() HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, GPIO_PIN_SET)#define OLED_RES_Clr() HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_RESET)
#define OLED_RES_Set() HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, GPIO_PIN_SET)#define OLED_CMD 0
#define OLED_DATA 1 void Lcd_Init(void);
void test_fill_entire_screen_blue(void);#define WHITE 0xFFFF
#define BLACK 0x0000
#define BLUE 0x001F
#define BRED 0XF81F
#define GRED 0x07E0
#define GBLUE 0X07FF
#define RED 0xF800
#define MAGENTA 0xF81F
#define GREEN 0x07E0
#define CYAN 0x7FFF
#define YELLOW 0xFFE0
#define BROWN 0XBC40
#define BRRED 0XFC07
#define GRAY 0X8430 #define DARKBLUE 0X01CF
#define LIGHTBLUE 0X7D7C
#define GRAYBLUE 0X5458 #define LIGHTGREEN 0X841F
#define LGRAY 0XC618 #define LGRAYBLUE 0XA651
#define LBBLUE 0X2B12 #endif