HC32F460填坑 - SPI POLLING SEND
HC32F460填坑 - SPI POLLING
- 总结
目前,小一点的系统很流行SPI接口的显示屏,有黑白TN,有彩色TFT,有高对比OLED,显示分辨率从3296到320240不等,这些显示屏成本低,接口少,易于驱动,所以非常受欢迎
硬件连线通常有以下:
复位RESET,片选CS,地址A0,时钟SCK,数据SDI,背光正负,电源正负,可以做到10PIN以内
因为显示屏的SPI在某些TIMING上会有一些要求,所以片选CS通常使用GPIO模拟,另外RESET,A0也是GPIO,SCK,SDI会使用硬件SPI,达到比较高的传输速率(50MHZ)
这是MCU驱动SPI显示屏的时序图,CS选中器件,DC(A0)决定写指令还是写参数,如果当前BYTE传输期间A0是LOW,表示写指令,如果A0是HIGH,表示写参数,传输完成后,CS再拉高
#include "board.h"#define LCDCODE_REGFLAG_DELAY 0xFFFE
#define LCDCODE_REGFLAG_END 0xFFFF
#define LCDCODE_DATA_LEN 32typedef struct
{uint16_t reg;uint16_t len;uint8_t dat[LCDCODE_DATA_LEN];
}lcd_code_t;static struct rt_semaphore spi3_tx_wait = {0x00};static void spi3_tx_dma_tc0_irq_cb(void)
{DMA_ClearIrqFlag(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, TrnCpltIrq);rt_sem_release(&spi3_tx_wait);
}static void spi3_dma_config(void)
{stc_dma_config_t stcDmaCfg;stc_irq_regi_conf_t stcIrqRegiConf;/* configuration structure initialization */MEM_ZERO_STRUCT(stcDmaCfg);/* Configuration peripheral clock */PWC_Fcg0PeriphClockCmd(SPI3_DMA_CLOCK_UNIT, Enable);PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS, Enable);/* Configure TX DMA */stcDmaCfg.u16BlockSize = 1u;stcDmaCfg.u16TransferCnt = 128;stcDmaCfg.u32SrcAddr = (uint32_t)(0);stcDmaCfg.u32DesAddr = (uint32_t)(&SPI3_UNIT->DR);stcDmaCfg.stcDmaChCfg.enSrcInc = AddressIncrease;stcDmaCfg.stcDmaChCfg.enDesInc = AddressFix;stcDmaCfg.stcDmaChCfg.enTrnWidth = Dma8Bit;stcDmaCfg.stcDmaChCfg.enIntEn = Enable;DMA_InitChannel(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, &stcDmaCfg);DMA_SetTriggerSrc(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, SPI3_DMA_TX_TRIG_SOURCE);/* Enable DMA. */DMA_Cmd(SPI3_DMA_UNIT, Enable);/* Set DMA block transfer complete IRQ */stcIrqRegiConf.enIRQn = IRQ_SPI3_DMA_TX;stcIrqRegiConf.pfnCallback = &spi3_tx_dma_tc0_irq_cb;stcIrqRegiConf.enIntSrc = INT_DMA1_TC0;enIrqRegistration(&stcIrqRegiConf);NVIC_SetPriority(stcIrqRegiConf.enIRQn, IRQ_PRIORITY_SPI3_DMA_TX);NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);
}static void bsp_spi3_init(void)
{stc_spi_init_t stcSpiInit;/* configuration structure initialization */MEM_ZERO_STRUCT(stcSpiInit);/* Configuration peripheral clock */PWC_Fcg1PeriphClockCmd(SPI3_UNIT_CLOCK, Enable);/* Configuration SPI structure */stcSpiInit.enClkDiv = SpiClkDiv32;stcSpiInit.enFrameNumber = SpiFrameNumber1;stcSpiInit.enDataLength = SpiDataLengthBit8;stcSpiInit.enFirstBitPosition = SpiFirstBitPositionMSB;stcSpiInit.enSckPolarity = SpiSckIdleLevelLow;stcSpiInit.enSckPhase = SpiSckOddSampleEvenChange;stcSpiInit.enReadBufferObject = SpiReadReceiverBuffer;stcSpiInit.enWorkMode = SpiWorkMode3Line;stcSpiInit.enTransMode = SpiTransOnlySend;stcSpiInit.enCommAutoSuspendEn = Disable;stcSpiInit.enModeFaultErrorDetectEn = Disable;stcSpiInit.enParitySelfDetectEn = Disable;stcSpiInit.enParityEn = Disable;stcSpiInit.enParity = SpiParityEven;stcSpiInit.enMasterSlaveMode = SpiModeMaster;stcSpiInit.stcDelayConfig.enSsSetupDelayOption = SpiSsSetupDelayCustomValue;stcSpiInit.stcDelayConfig.enSsSetupDelayTime = SpiSsSetupDelaySck1;stcSpiInit.stcDelayConfig.enSsHoldDelayOption = SpiSsHoldDelayCustomValue;stcSpiInit.stcDelayConfig.enSsHoldDelayTime = SpiSsHoldDelaySck1;stcSpiInit.stcDelayConfig.enSsIntervalTimeOption = SpiSsIntervalCustomValue;stcSpiInit.stcDelayConfig.enSsIntervalTime = SpiSsIntervalSck1PlusPck2;SPI_Init(SPI3_UNIT, &stcSpiInit);spi3_dma_config();SPI_Cmd(SPI3_UNIT, Enable); rt_sem_init(&spi3_tx_wait, "spi3tx", 0, RT_IPC_FLAG_FIFO);
}static void lcd_spi_send(uint8_t dat)
{ while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSendBufferEmpty)){;}SPI_SendData8(SPI3_UNIT, dat);while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSpiIdle)){;}
}
/*
static void lcd_spi_trans(uint8_t *dat, uint16_t len)
{DMA_SetSrcAddress (SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, (uint32_t)dat);DMA_SetTransferCnt(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, len);LCD_CS_L;DMA_ChannelCmd(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, Enable);SPI_Cmd(SPI3_UNIT, Enable); rt_sem_take(&spi3_tx_wait, RT_WAITING_FOREVER);LCD_CS_H;SPI_Cmd(SPI3_UNIT, Disable);DMA_ChannelCmd(SPI3_DMA_UNIT, SPI3_DMA_TX_CHANNEL, Disable);
}
*/
static void delayms(uint16_t ms)
{rt_thread_delay(ms);
}static void write_codetable(lcd_code_t *code, uint32_t count)
{lcd_code_t *pcode = code;//传输指令和参数for (uint32_t i = 0; i < count; i++){ if (pcode->reg == LCDCODE_REGFLAG_END){ //结束跳出break;}else if (pcode->reg == LCDCODE_REGFLAG_DELAY){ //延时MSdelayms(pcode->len);}else{ //常规发送指令LCD_A0_L;LCD_CS_L;lcd_spi_send(pcode->reg);for (uint32_t j = 0; j < pcode->len; j++){LCD_A0_H;lcd_spi_send(pcode->dat[j]);}LCD_CS_H;}pcode++; //下一个寄存器}
}//初始化指令表
static lcd_code_t st7789v_initcode[] = {
//{0x11, 0, {0x00} },
//{LCDCODE_REGFLAG_DELAY, 120, {0x00}},
//{0xb2, 5, {0x0c, 0x0c, 0x00, 0x33, 0x33} },
//{0xb7, 1, {0x35} },
//{0xbb, 1, {0x35} },
//{0xc0, 1, {0x2c} },
//{0xc2, 1, {0x01} },
//{0xc3, 1, {0x0b} },
//{0xc4, 1, {0x20} },
//{0xc6, 1, {0x0f} },
//{0xd0, 2, {0xa4, 0xa1} },
//{0xe0, 14, {0xd0, 0x00, 0x02, 0x07, 0x0b, 0x1a, 0x32, 0x54, 0x40, 0x29, 0x12, 0x12, 0x12, 0x17} },
//{0xe1, 14, {0xd0, 0x00, 0x02, 0x07, 0x05, 0x25, 0x2d, 0x44, 0x45, 0x1c, 0x18, 0x16, 0x1c, 0x1d} },
//{0x36, 1, {0x00} },
{0x3a, 1, {0x05} },
//{0x29, 0, {0x00} },
{LCDCODE_REGFLAG_END, 0x00, {0x00} }
};/*显示区域设置*/
static lcd_code_t st7789v_blockcode[] = {
{0x2a, 4, {0x00, 0x00, 0x00, 0x00} },
{0x2b, 4, {0x00, 0x00, 0x00, 0x00} },
{0x2c, 0, {0x00} },
{LCDCODE_REGFLAG_END, 0x00, {0x00} }
};static void write_block(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{st7789v_blockcode[0].dat[0] = 0x00;st7789v_blockcode[0].dat[1] = x0;st7789v_blockcode[0].dat[2] = 0x00;st7789v_blockcode[0].dat[3] = x1;st7789v_blockcode[1].dat[0] = y0 >> 8;st7789v_blockcode[1].dat[1] = y0 & 0xff;st7789v_blockcode[1].dat[2] = y1 >> 8;st7789v_blockcode[1].dat[3] = y1 & 0xff;write_codetable(st7789v_blockcode, 5);
}void st7789v_init(void)
{stc_port_init_t stcPortInit;MEM_ZERO_STRUCT(stcPortInit);PWC_Fcg1PeriphClockCmd(SPI3_UNIT_CLOCK, Enable);/* Configuration SPI pin */PORT_SetFunc(SPI3_NSS_PORT, SPI3_NSS_PIN, Func_Gpio, Disable); PORT_SetFunc(SPI3_SCK_PORT, SPI3_SCK_PIN, SPI3_SCK_FUNC, Disable);PORT_SetFunc(SPI3_SDI_PORT, SPI3_SDI_PIN, SPI3_SDI_FUNC, Disable);PORT_SetFunc(LCD_RST_PORT, LCD_RST_PIN, Func_Gpio, Disable);PORT_SetFunc(LCD_A0_PORT, LCD_A0_PIN, Func_Gpio, Disable);stcPortInit.enPinMode = Pin_Mode_Out;stcPortInit.enPinDrv = Pin_Drv_H;PORT_Init(LCD_RST_PORT, LCD_RST_PIN, &stcPortInit);PORT_Init(LCD_A0_PORT, LCD_A0_PIN, &stcPortInit);PORT_Init(LCD_CS_PORT, LCD_CS_PIN, &stcPortInit);bsp_spi3_init();LCD_RST_H;LCD_A0_H;LCD_CS_H;delayms(10);LCD_RST_L;delayms(10);LCD_RST_H;delayms(120);write_codetable(st7789v_initcode, sizeof(st7789v_initcode)/sizeof(lcd_code_t));
}void st7789v_test(void)
{LCD_RST_H;LCD_A0_H;LCD_CS_H;delayms(10);LCD_RST_L;delayms(10);LCD_RST_H;delayms(120);write_codetable(st7789v_initcode, sizeof(st7789v_initcode)/sizeof(lcd_code_t));delayms(10);//write_block(1, 1, 128, 300);
}
以上代码是测试MCU发送指令0x3a, 参数0x05,SPI发送代码参考HC32F460 DDL库hc32f460_ddl_Rev2.2.0.zip
static void lcd_spi_send(uint8_t dat)
{ while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSendBufferEmpty)){;}SPI_SendData8(SPI3_UNIT, dat);
}LCD_A0_L;LCD_CS_L;lcd_spi_send(pcode->reg);for (uint32_t j = 0; j < pcode->len; j++){LCD_A0_H;lcd_spi_send(pcode->dat[j]);}LCD_CS_H;
每次发送BYTE之前判断SPI BUFFER是否为空,如果空,写入数据,实施证明,BUFFER空,仅仅是BUFFER空,数据还在SPI硬件里发送着,根本就没结束,而且参考手册里面,SPI和发送相关的状态位也就这个BUFFER EMPTY,而不像之前STM,还有一个发送结束COMPLETE状态位。
看下面波形,第一个字节8个CLK还没结束,CS,A0就已经变高了,造成实际传输无效
修改如下:
static void lcd_spi_send(uint8_t dat)
{ while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSendBufferEmpty)){;}SPI_SendData8(SPI3_UNIT, dat);while (Reset == SPI_GetFlag(SPI3_UNIT, SpiFlagSpiIdle)){;}
}LCD_A0_L;LCD_CS_L;lcd_spi_send(pcode->reg);for (uint32_t j = 0; j < pcode->len; j++){LCD_A0_H;lcd_spi_send(pcode->dat[j]);}LCD_CS_H;
每次写数据之前判断BUFFER是否空,每次写数据之后,等待SPI传输结束
波形上已经达到我们时序的要求,所以,直接参考当前库代码,至少对于驱动LCD,是有问题的。
总结
有异常,外设无法工作时候,我们要习惯于分析波形,从时序中排查问题,然后再尝试解决问题。
HC32F460填坑 - SPI POLLING SEND相关推荐
- 【日常填坑】之ajax请求laravel的api接口
关于作者 程序开发人员,不拘泥于语言与技术,目前主要从事PHP和前端开发,使用Laravel和VueJs,App端使用Apicloud混合式开发.合适和够用是最完美的追求. 个人网站:http://w ...
- 填坑之PHP的yield和协程在一起的日子里
首先是,这是我第一次把公众号文章复制粘贴到github来. 其次是,很久很久之前,我挖了一个yield的一个坑,自己挖的坑自己填,不然迟早会把自己埋掉. 最后是,如果想看之前那个坑,请发送" ...
- kylin4.0.1安装与填坑
kylin4.0.1安装 解压安装包 将hdfs-site.xml,core-site.xml,hive-site.xml,spark-defaults.conf文件添加到/$KYLIN_HOME/c ...
- 开源android项目到jcenter,手把手教你将Android项目开源到JCenter两种方式以及挖坑和填坑(一)...
- 前言 开发中,或多或少都会用到无私的程序猿分享的开源项目,Androidstudio中使用开源也很方便 例如家喻户晓的Rxjava,只需要一句话compile 'io.reactivex:rxja ...
- [单片机][at32][填坑日记] [USB卡包] usb上电过程中快速发包导致卡包(终章)
文章目录 一.原因造成如下: 1. 串口外设优先级大于USB中断(USBOTG_IRQn),导致串口数据打断USB,致使PC或MCU丢包. 2. 当PC请求usb设备描述符字符串时,USB的其他通道不 ...
- java.lang.OutOfMemoryError:GC overhead limit exceeded填坑心得
该文章出自:http://www.cnblogs.com/hucn/p/3572384.html 分析工具:http://www.blogjava.net/jjshcc/archive/2014/03 ...
- 20150726 填坑日记
三中内填坑: 1. 组合数递推什么的 C(m,n)=C(m,n-1)+C(m-1,n-1).填了个大坑,以前没认真听课QAQ 2. 裸题过河卒 3. 缺角正方形摆放车统计,分上下部分,枚举上部分放几个 ...
- 传统行业转型微服务的挖坑与填坑
原文:传统行业转型微服务的挖坑与填坑 一.微服务落地是一个复杂问题,牵扯到IT架构,应用架构,组织架构多个方面 在多家传统行业的企业走访和落地了微服务之后,发现落地微服务是一个非常复杂的问题,甚至都不 ...
- 开发工具总结(4)之Android Studio3.0填坑指南
序言 Android Studio 3.0 上篇讲了: 全面总结Android Studio2.X的填坑指南 这篇讲一下AS3.0的坑.. 作为这个世界上走在最前沿的生物"猿",怎 ...
最新文章
- ATS中的动态回源插件stale-while-revalidate调研
- vivado烧录flash过程
- 以比特币现金(BCH)为核心的慈善经济体系革新业态
- html 超出部分被遮挡,div被iframe遮住的几种情况及解决方法
- 全球通吃5G!高通发布最新骁龙865、7系芯片,小米、OPPO将实现首发
- 2017-06-09 问题
- [crypto]-53-openssl命令行的使用(aes/rsa签名校验/rsa加密解密/hmac)
- POJ 1287 Prim算法模板
- ubuntu ifconfig命令找不到_那些年踩过的坑--无法使用MobaXterm远程登录Ubuntu
- 第六章:nginx实现动静分离
- vmware虚拟机的tomcat启动以后,主机无法访问
- mysql导出kml_谷歌地图如何导出kml文件 导出kml文件教程
- Android学习之——ListView下拉刷新
- 自动优化工具Black帮你写出规范漂亮的python代码
- ArduinoUNO实战-第九章-光敏电阻或亮度传感器
- 2017中兴捧月算法精英挑战赛-迪杰斯特拉
- 华为网络设备介绍及基础配置命令
- excel学习-日期计算函数DATEDIF函数(计算相隔年数、月数、天数)
- 获取星期--蔡勒公式
- 为什么青少年一定要学Python?