在点亮k210后,我就开始思考怎么压榨出其中的全部性能,通过增加了几个sdk的驱动函数,我成功的从LCD图片传输的任务中为CPU解放了大量时间。如何点亮的可以看K210 Standalone SDK,用C点亮野火K210-优快云博客。
检查SDK可以发现,在spi_send_data_normal_dma这个函数中,启动dma通道后,紧接着就调用了dmac_wait_done来等待dma传输完成。但这完全是没有必要的,我们可以通过中断来完成后续寄存器的处理,让CPU能处理其它任务,而不必等待。
为此我实现了一个不等待的spi传输。
typedef struct _local_callback_data_t
{
volatile spi_t *spi_handle;
sysctl_dma_channel_t channel_num;
spi_device_num_t spi_num;
void (*cb)(void);
} local_callback_data_t;
static local_callback_data_t local_callback_data;
static int dmac_irq_callback(void *ctx)
{
local_callback_data_t *data = (local_callback_data_t *)ctx;
dmac_irq_unregister(data->channel_num);
plic_irq_unregister(IRQN_SPI0_INTERRUPT + data->spi_num);
volatile spi_t *spi_handle = data->spi_handle;
uint32_t cnt = 0;
while((spi_handle->sr & 0x05) != 0x04)
;
spi_handle->ser = 0x00;
spi_handle->ssienr = 0x00;
if(data->cb)
{
data->cb();
}
return 0;
}
/// @brief 发送数据,DMA启动后不等待,数据格式为SPI_TRANS_INT,
/// 但由用户在外部提前打包好,本函数不进行打包
void spi_send_data_normal_dma_no_blocking(dmac_channel_number_t channel_num, spi_device_num_t spi_num, spi_chip_select_t chip_select,
const void *tx_buff, size_t tx_len, void (*callback)(void))
{
configASSERT(spi_num < SPI_DEVICE_MAX && spi_num != 2);
spi_set_tmod(spi_num, SPI_TMOD_TRANS);
volatile spi_t *spi_handle = spi[spi_num];
uint32_t *buf = (uint32_t *)tx_buff;
spi_handle->dmacr = 0x2; /*enable dma transmit*/
spi_handle->ssienr = 0x01;
sysctl_dma_select((sysctl_dma_channel_t)channel_num, SYSCTL_DMA_SELECT_SSI0_TX_REQ + spi_num * 2);
local_callback_data.cb = callback;
local_callback_data.spi_handle = spi_handle;
local_callback_data.channel_num = channel_num;
local_callback_data.spi_num = spi_num;
dmac_irq_register(channel_num, dmac_irq_callback, &local_callback_data, 1);
// plic_irq_register(IRQN_SPI0_INTERRUPT + spi_num, dmac_irq_callback, &local_callback_data);
dmac_set_single_mode(channel_num, buf, (void *)(&spi_handle->dr[0]), DMAC_ADDR_INCREMENT, DMAC_ADDR_NOCHANGE,
DMAC_MSIZE_4, DMAC_TRANS_WIDTH_32, tx_len);
spi_handle->ser = 1U << chip_select;
}
接口参数与spi_send_data_normal_dma基本相同,但是删去了最后的spi_transfer_width参数,改为一个回调函数,这以为这它只能以4字节为单位传输。由于函数比较简陋,因此想要知道传输是否完成,只能通过在回调函数中进行操作。
然后在st7789.c文件中,也要添加相应的函数。
void tft_write_word_no_blocking(uint32_t *data_buf, uint32_t length, void (*callback)(void))
{
set_dcx_data();
if(!standard_spi)
{
spi_init(SPI_CHANNEL, SPI_WORK_MODE_0, SPI_FF_OCTAL, 32, 0);
spi_init_non_standard(SPI_CHANNEL, 0 /*instrction length*/, 32 /*address length*/, 0 /*wait cycles*/,
SPI_AITM_AS_FRAME_FORMAT /*spi address trans mode*/);
spi_send_data_normal_dma_no_blocking(SPI_DMA_CH, SPI_CHANNEL, LCD_SPI_SLAVE_SELECT, data_buf, length, callback);
} else
{
spi_init(SPI_CHANNEL, standard_work_mode, SPI_FF_STANDARD, 32, 0);
spi_send_data_normal_dma_no_blocking(SPI_DMA_CH, SPI_CHANNEL, LCD_SPI_SLAVE_SELECT, data_buf, length, callback);
}
}
实际就是原本的函数,稍作修改就行了。
最后实现lcd绘制函数即可。
static void mcu_lcd_draw_picture_no_blocking(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, uint8_t *ptr, void (*callback)(void))
{
g_lcd_display_buff = (uint16_t *)ptr;
lcd_set_area(x1, y1, x1 + width - 1, y1 + height - 1);
g_pixs_draw_pic_size = width * height;
if(g_pixs_draw_pic_size % 2)
{
tft_write_half((uint16_t *)ptr, g_pixs_draw_pic_size);
}
if(g_pixs_draw_pic_size > 0)
{
// tft_write_word((uint32_t *)ptr, g_pixs_draw_pic_size / 2);
tft_write_word_no_blocking((uint32_t *)ptr, g_pixs_draw_pic_size / 2, callback);
// tft_write_half(g_lcd_display_buff, g_pixs_draw_pic_size);
// tft_write_half_packed_no_blocking((uint32_t *)g_lcd_display_buff, g_pixs_draw_pic_size, callback);
}
}