硬汉嵌入式论坛

 找回密码
 立即注册
查看: 112|回复: 3
收起左侧

[技术讨论] 【问题贴】CH32V307使用LVGL+SPI+DMA+中断 方式,DMA中断只能进入一次

[复制链接]

1

主题

1

回帖

4

积分

新手上路

积分
4
发表于 6 天前 | 显示全部楼层 |阅读模式
SPI初始化

[C] 纯文本查看 复制代码
void SPI1_Init(void)	
{
	GPIO_InitTypeDef GPIO_InitStructure={0};
	SPI_InitTypeDef SPI_InitStructure={0};
	
	//Master:SPI1_SCK(PA5)\SPI1_MISO(PA6)\SPI1_MOSI(PA7).
	
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE );
 
    // 配置SCLK引脚(PA5)
    GPIO_InitStructure.GPIO_Pin = SPI_SCLK_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    // 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  // 高速模式
    GPIO_Init(SPI_PORT, &GPIO_InitStructure);
    
    // 配置MISO引脚(PA6)
    GPIO_InitStructure.GPIO_Pin = SPI_MISO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  // 浮空输入模式
    GPIO_Init(SPI_PORT, &GPIO_InitStructure);
    
    // 配置MOSI引脚(PA7)
    GPIO_InitStructure.GPIO_Pin = SPI_MOSI_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    // 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  // 高速模式
    GPIO_Init(SPI_PORT, &GPIO_InitStructure);

	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;					//数据宽度
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;	//双线双向全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;						//主模式
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;							//SPI极性:低极性
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;						//SPI相位:选择第一个 时钟边沿采样 极性和相位决定SPI模式0
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;							//NSS:选择软件控制
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;	//波特率分频:2
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;					//先行位:高位先行
	SPI_InitStructure.SPI_CRCPolynomial = 7;							//CRC多项式,用不到,给7
	SPI_Init( SPI1, &SPI_InitStructure );	//初始化


    // 使能 SPI1 的 DMA 发送请求
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
    
    // 使能SPI
	SPI_Cmd( SPI1, ENABLE );

}



DMA初始化
[C] 纯文本查看 复制代码
#include "dma.h"
#include "lcd.h"

void MYDMA_Init()
{
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;  // 添加 NVIC 配置结构体

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);// 开启DMA时钟
    
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI1->DATAR;   //搬运的外设地址
   // DMA_InitStructure.DMA_MemoryBaseAddr = MemAddr;                 //搬运的内存地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;// 方向:从内存到外设
    DMA_InitStructure.DMA_BufferSize = 0;        // 传输大小   
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;// 外设地址不增        
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;// 内存地址自增
    // DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;// 外设数据单位为16位
    // DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;// 内存数据单位为字位
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;// 外设数据单位为16位
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;// 内存数据单位为字位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;// DMA模式,一次或者循环模式
    //    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ;// DMA模式,一次或者循环模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;// 优先级:中    
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;// 禁止内存到内存的传输
    DMA_Init(DMA1_Channel3, &DMA_InitStructure);// 配置DMA通道    

     // ----------------------
    // **新增:DMA 中断使能**
    // ----------------------
    DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE);  // 使能传输完成中断

    // ----------------------
    // **新增:NVIC 中断配置**
    // ----------------------
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel3_IRQn;  // 选择 DMA1 通道 3 中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  // 抢占优先级(0-3,0 最高)
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        // 子优先级(0-1,0 最高)
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           // 使能中断通道
    NVIC_Init(&NVIC_InitStructure);

    DMA_ClearFlag(DMA1_FLAG_TC3);
    DMA_Cmd(DMA1_Channel3,DISABLE);// 禁用DMA,在每次传输前使能
}

// 使能 DMA1 通道 3 传输(假设使用通道 3,根据实际情况调整)
void MYDMA_ENABLE(uint16_t size)
{
    // 禁用 DMA1 通道 3
    DMA_Cmd(DMA1_Channel3, DISABLE);

    // 设置 DMA1 通道 3 的传输数据长度
    DMA_SetCurrDataCounter(DMA1_Channel3, size);

    // 启用 DMA1 通道3
    DMA_Cmd(DMA1_Channel3, ENABLE);
}

void DMA_Start(uint16_t Size)
{
    LCD_CS_CLR          // 拉低CS(片选有效)
    LCD_DC_SET          // 设置为数据模式
    SPI_I2S_DMACmd(SPI1,SPI_I2S_DMAReq_Tx,ENABLE);
    MYDMA_ENABLE(Size);
    // 不再等待DMA完成,改为中断处理
    // while(DMA_GetFlagStatus(DMA1_FLAG_TC3)==RESET);     //等待发送完毕
    // DMA_ClearFlag(DMA1_FLAG_TC3);//清除通道3传输完成标志
}




lvgl部分
[C] 纯文本查看 复制代码
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
    if(!disp_flush_enabled) {
        lv_disp_flush_ready(disp_drv);
        return;
    }

    // if(disp_flush_enabled) {
    //     /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/

    //     int32_t x;
    //     int32_t y;
    //     for(y = area->y1; y <= area->y2; y++) {
    //         for(x = area->x1; x <= area->x2; x++) {
    //             /*Put a pixel to the display. For example:*/
    //             GUI_DrawPoint(x, y, color_p->full);         //绑定画点函数
    //             /*put_px(x, y, *color_p)*/
    //             color_p++;
    //         }
    //     }
    // }

    uint16_t w = (area->x2 - area->x1 + 1);
    uint16_t h = (area->y2 - area->y1 + 1);
    uint32_t Size = w * h * 2;

    LCD_SetWindows(area->x1, area->y1, area->x2, area->y2);//设置光标位置 
    DMA1_Channel3->MADDR = (uint32_t) color_p;

    // 保存当前驱动指针,供DMA完成回调使用
    current_drv = disp_drv;
    
    // 使能 DMA1 通道 3
    DMA_Start(Size);

    //lv_disp_flush_ready(disp_drv);
}

// DMA传输完成中断处理函数
void DMA1_Channel3_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_IT_TC3)) {
        // 清除中断标志
        DMA_ClearITPendingBit(DMA1_IT_TC3);
        
        // 拉高CS(片选无效)
        LCD_CS_SET;
        
        // 通知LVGL刷新完成
        if(current_drv != NULL) {
            lv_disp_flush_ready(current_drv);
            current_drv = NULL;
        }

        DMA_Cmd(DMA1_Channel3, DISABLE);  // 禁用通道
        DMA_SetCurrDataCounter(DMA1_Channel3, 0);  // 清除剩余传输量(可选)
    }
}



采用的双缓冲

[C] 纯文本查看 复制代码
    /* Example for 2) */
    static lv_disp_draw_buf_t draw_buf_dsc_2;
    static lv_color_t buf_2_1[MY_DISP_HOR_RES * 15];                        /*A buffer for 10 rows*/
    static lv_color_t buf_2_2[MY_DISP_HOR_RES * 15];                        /*An other buffer for 10 rows*/
    lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 15);   /*Initialize the display buffer*/

    // /* Example for 3) also set disp_drv.full_refresh = 1 below*/
    // static lv_disp_draw_buf_t draw_buf_dsc_3;
    // static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES];            /*A screen sized buffer*/
    // static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES];            /*Another screen sized buffer*/
    // lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2,
    //                       MY_DISP_VER_RES * LV_VER_RES_MAX);   /*Initialize the display buffer*/

    /*-----------------------------------
     * Register the display in LVGL
     *----------------------------------*/

    static lv_disp_drv_t disp_drv;                         /*Descriptor of a display driver*/
    lv_disp_drv_init(&disp_drv);                    /*Basic initialization*/

    /*Set up the functions to access to your display*/

    /*Set the resolution of the display*/
    disp_drv.hor_res = MY_DISP_HOR_RES;
    disp_drv.ver_res = MY_DISP_VER_RES;

    /*Used to copy the buffer's content to the display*/
    disp_drv.flush_cb = disp_flush;

    /*Set a display buffer*/
    disp_drv.draw_buf = &draw_buf_dsc_2;

    /*Required for Example 3)*/
    //disp_drv.full_refresh = 1;

    /* Fill a memory array with a color if you have GPU.
     * Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.
     * But if you have a different GPU you can use with this callback.*/
    //disp_drv.gpu_fill_cb = gpu_fill;

    /*Finally register the driver*/
    lv_disp_drv_register(&disp_drv);
}



使用单缓冲+SPI发送等待的方法可以实现,不过帧率只有13。
修改DMA和SPI的 传输步长等, 随机出现条纹等。。
DMA 中断的方法, 只能进入一次中断, DMA能发送两次貌似。/
如何解决!!!!

回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
116197
QQ
发表于 4 天前 | 显示全部楼层
非DMA方式的双缓冲是否正常
回复

使用道具 举报

1

主题

1

回帖

4

积分

新手上路

积分
4
 楼主| 发表于 3 天前 | 显示全部楼层
eric2013 发表于 2025-6-6 07:18
非DMA方式的双缓冲是否正常

你好,我已经解决了,原因是 沁恒的CH32单片机是 RISC-V内核, 其中断需要添加如下声明:
[C] 纯文本查看 复制代码
void DMA1_Channel3_IRQHandler(void)__attribute__((interrupt("WCH-Interrupt-fast")));
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
116197
QQ
发表于 前天 10:14 | 显示全部楼层
刘振杰 发表于 2025-6-7 15:55
你好,我已经解决了,原因是 沁恒的CH32单片机是 RISC-V内核, 其中断需要添加如下声明:
[mw_shl_code= ...

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-6-10 05:01 , Processed in 0.245204 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表