基于MM32的电子墨水屏应用及SPI发送9-BIT数据的实现

电子墨水屏
回忆当中,第一接触电子墨水屏还是从Kindle这个电子书开始的,起初不知道它是怎么个显示原理,拿到手后体验显示刷新界面时曾一度无法适应,感觉很**肋,就是为了省电么……后来百度电子墨水屏的显示原理以及结构组成后,感觉相对于传统的LCD显示而言,优势是相当的明显(视觉感观、待机功耗);从开始的整体刷新显示,到后面可以局部刷新显示、从开始的单色显示,到后面的彩色(多色)显示,灰度显示,对于电子墨水屏的显示技术都在不断的发展,其应用领域也越来越多,我们常见的电子标签、某宝上热卖的背单词的产品都是使用电子墨水屏实现的。

MM32F3270低功耗
MM32F3270系列芯片有六种低功耗模式,包括低功耗运行(Lower Power Run)、睡眠(Sleep)、低功耗睡眠(Low Power Sleep)、停机(Stop)、深度停机(Deep Stop)和待机模式(Standby)。结合电子墨水屏应用实现的电子标签,只有当MCU在更新显示的时候才需要消耗电流,而在低功耗模式时,MCU的工作电流则可以降达到微安(uA)级别,可以在极大程度上延长产品的使用寿命。用户可以根据电源消耗不同、唤醒时间不同、唤醒源不同,结合应用需求,来选择最佳的低功耗模式,具体的低功耗模式列表如下所示:

 

MM32F3270 SPI
MM32F3270系列MCU最多支持3路SPI接口,而且SPI接口支持接收和发送1~32位数据同时进行,并且支持LSB格式和MSB格式的数据;结合这一特性,通过设置GCTL寄存器的DW8_32位来使32位数据都有效,通过配置EXTCTL寄存来控制SPI数据位长度,与电子墨水屏的SPI接口进行数据通讯,实现对电子墨水屏的显示控制。

基于MM32F3270的SPI FLASH下载算法
之前我们有介绍基于NOR FLASH的下载算法,SPI FLASH与NOR FLASH的访问方式不同,所以在实现把数据下载到SPI FLASH时,需要按照如下的操作步骤:
首先通过KEIL自带的下载算法工程模板,基于MM32F3270去新建一个SPI FLASH下载算法工程,这个可以参考之前的NOR FLASH下载算法工程,唯一的区别就是一个是FSMC去操作NOR FLASH,一个是通过SPI来操作SPI FLASH,其思路是一样的;
其次是基于MM32F3270新建一个空的工程,只需要启动文件的最简工程,官方的驱动库文件都可以不用添加到工程当中;然后将需要下载到SPI FLASH的资源添加这个工程中,并将这个资源的存储地址指向自己定义的存储空间,然后在程序中,记得一定要引用(使用)这个资源,才不会被KEIL优化掉,然后编译进行下载;下载之前SPI FLASH中就有我们所需要的资源数据了,但MCU中也有一个空跑的,无用的程序;
最后,就是下载一个我们通用的应用程序到MCU内部FLASH中,复位并运行;这个时候,应用程序就可以读取SPI FLASH中的资源数据了;

当然SPI FLASH的下载方式很多,有专门的烧录工具,也可能通过J-LINK来烧写,更有甚者通过MCU的应用程序来烧写SPI FLASH,而通过FLM下载算法的方式也仅仅是其中一种。

原理图
基于MM32F3270的最小系统,预留SWD程序调试/烧录接口和BOOT配置拨动开关,带有一颗用户LED指示灯和一个SPI FLASH存储芯片,SPI FLASH存储芯片预留SPI烧录接口;对于SPI FLASH的作用是存储电子墨水屏的显示数据,预留的烧录接口可以使用SPI FLASH专用的烧录工具,也可以使用J-LINK进行烧录,或者是通过MCU的下载算法来下载/烧录数据。

 

开发板供5V电源,通过LDO转换成3.3V作为系统工作电压;添加CH340(USB与UART TTL)方便程序调试和打印运行日志信息;参考官方的电子墨水屏参考电路,间隔0.5毫米的24PIN FPC软排线,支持佳显的2.13寸、2.9寸、4.2寸等多个尺寸的电子墨水屏;通过对R3电阻的选焊可以切换电子墨水屏的通讯控制方式;


PCB设计

 

 


回板焊接


整机效果

 

 

要是再有个外壳,就更NICE啦

MM32F3270 SPI驱动电子墨水屏实现代码:

实现方式1:GPIO
使用GPIO的高低电平设置,适当的添加延时处理,来达到电子墨水屏的SPI操作时序要求,完成MCU与电子墨水屏之前的数据通讯,具体的配置和实现代码如下所示:
复制
/*******************************************************************************

 * [url=home.php?mod=space&uid=247401]@brief[/url]       

 * @param       

 * @retval      

 * [url=home.php?mod=space&uid=93590]@Attention[/url]   

*******************************************************************************/

void EPAPER_InitSPI1(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHBPeriphClockCmd(EPAPER_CS_RCC, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = EPAPER_CS_PIN;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;

    GPIO_Init(EPAPER_CS_GPIO, &GPIO_InitStructure);

    GPIO_WriteBit(EPAPER_CS_GPIO, EPAPER_CS_PIN, Bit_SET);

    RCC_AHBPeriphClockCmd(EPAPER_SCK_RCC, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = EPAPER_SCK_PIN;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;

    GPIO_Init(EPAPER_SCK_GPIO, &GPIO_InitStructure);

    RCC_AHBPeriphClockCmd(EPAPER_SDI_RCC, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = EPAPER_SDI_PIN;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;

    GPIO_Init(EPAPER_SDI_GPIO, &GPIO_InitStructure);

}

/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_SPI_WriteData(uint32_t Data)

{

    uint8_t i = 0;

    GPIO_WriteBit(EPAPER_SCK_GPIO, EPAPER_SCK_PIN, Bit_RESET);

    for(i = 0; i < 8; i++)

    {

        if(Data & (0x80 >> i))

        {

            GPIO_WriteBit(EPAPER_SDI_GPIO, EPAPER_SDI_PIN, Bit_SET);

        }

        else

        {

            GPIO_WriteBit(EPAPER_SDI_GPIO, EPAPER_SDI_PIN, Bit_RESET);

        }

        GPIO_WriteBit(EPAPER_SCK_GPIO, EPAPER_SCK_PIN, Bit_SET);

        GPIO_WriteBit(EPAPER_SCK_GPIO, EPAPER_SCK_PIN, Bit_RESET);

    }

}

/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_WaitBusy(void)

{

    while(GPIO_ReadInputDataBit(EPAPER_BUSY_GPIO, EPAPER_BUSY_PIN));

}

/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_WriteCMD(uint8_t Command)

{

    GPIO_WriteBit(EPAPER_CS_GPIO, EPAPER_CS_PIN, Bit_RESET);

    GPIO_WriteBit(EPAPER_DC_GPIO, EPAPER_DC_PIN, Bit_RESET);

    EPAPER_SPI_WriteData(Command);

    GPIO_WriteBit(EPAPER_CS_GPIO, EPAPER_CS_PIN, Bit_SET);

}

/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_WriteDAT(uint8_t Data)

{

    GPIO_WriteBit(EPAPER_CS_GPIO, EPAPER_CS_PIN, Bit_RESET);

    GPIO_WriteBit(EPAPER_DC_GPIO, EPAPER_DC_PIN, Bit_SET);

    EPAPER_SPI_WriteData(Data);

    GPIO_WriteBit(EPAPER_CS_GPIO, EPAPER_CS_PIN, Bit_SET);

}

实现方式2:8位标准SPI + DC命令/数据控制线
将原理图上的R3电阻焊接上,则MCU与电子墨水屏之间则是通过DC引脚的高低电平来区分数据还是指令,SPI此时需传输标准的8位数据,使MCU与电子墨水屏之间建立通讯,具体的配置和实现代码如下所示:
复制
/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_InitSPI1(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;

    SPI_InitTypeDef  SPI_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_SPI1, ENABLE);

    SPI_StructInit(&SPI_InitStructure);

    SPI_InitStructure.SPI_Mode              = SPI_Mode_Master;

    SPI_InitStructure.SPI_DataSize          = SPI_DataSize_8b;

    SPI_InitStructure.SPI_DataWidth         = SPI_DataWidth_8b;

    SPI_InitStructure.SPI_CPOL              = SPI_CPOL_Low;

    SPI_InitStructure.SPI_CPHA              = SPI_CPHA_1Edge;

    SPI_InitStructure.SPI_NSS               = SPI_NSS_Soft;

    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;

    SPI_InitStructure.SPI_FirstBit          = SPI_FirstBit_MSB;

    SPI_Init(SPI1, &SPI_InitStructure);

    SPI_Cmd(SPI1, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_5);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_5);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_5);

    GPIO_StructInit(&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;

    GPIO_Init(GPIOA, &GPIO_InitStructure);

    SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Tx);

}

/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_SPI_WriteData(uint32_t Data)

{

    __asm volatile("cpsid i");

    WRITE_REG(SPI1->TDR, Data);

    __asm volatile("cpsie i");

    while(!SPI_GetFlagStatus(SPI1, SPI_FLAG_TXEPT));

}

/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_WaitBusy(void)

{

    while(GPIO_ReadInputDataBit(EPAPER_BUSY_GPIO, EPAPER_BUSY_PIN));

}

/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_WriteCMD(uint8_t Command)

{

    SPI_CSInternalSelected(SPI1, ENABLE);

    GPIO_WriteBit(EPAPER_DC_GPIO, EPAPER_DC_PIN, Bit_RESET);

    EPAPER_SPI_WriteData(Command);

    SPI_CSInternalSelected(SPI1, DISABLE);

}

/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_WriteDAT(uint8_t Data)

{

    SPI_CSInternalSelected(SPI1, ENABLE);

    GPIO_WriteBit(EPAPER_DC_GPIO, EPAPER_DC_PIN, Bit_SET);

    EPAPER_SPI_WriteData(Data);

    SPI_CSInternalSelected(SPI1, DISABLE);

}

实现方式3:9位SPI
将原理图上的R3电阻去除掉,此时DC引脚需要一直处于低电平状态,电子墨水屏不根据DC引脚的电平状态来区分数据或指令,此时要求SPI通讯必须传输9位数据位,第一位的0或是1来代替DC引脚的功能,区分后面的8位数据是数据还是指令,具体的配置和实现代码如下所示:
复制
/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_InitSPI1(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;

    SPI_InitTypeDef  SPI_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_SPI1, ENABLE);

    SPI_StructInit(&SPI_InitStructure);

    SPI_InitStructure.SPI_Mode              = SPI_Mode_Master;

    SPI_InitStructure.SPI_DataSize          = SPI_DataSize_32b;

    SPI_InitStructure.SPI_DataWidth         = SPI_DataWidth_9b;

    SPI_InitStructure.SPI_CPOL              = SPI_CPOL_Low;

    SPI_InitStructure.SPI_CPHA              = SPI_CPHA_1Edge;

    SPI_InitStructure.SPI_NSS               = SPI_NSS_Soft;

    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;

    SPI_InitStructure.SPI_FirstBit          = SPI_FirstBit_MSB;

    SPI_Init(SPI1, &SPI_InitStructure);

    SPI_Cmd(SPI1, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_5);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_5);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_5);

    GPIO_StructInit(&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;

    GPIO_Init(GPIOA, &GPIO_InitStructure);

    SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Tx);

}

/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_SPI_WriteData(uint32_t Data)

{

    __asm volatile("cpsid i");

    WRITE_REG(SPI1->TDR, Data);

    __asm volatile("cpsie i");

    while(!SPI_GetFlagStatus(SPI1, SPI_FLAG_TXEPT));

}

/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_WaitBusy(void)

{

    while(GPIO_ReadInputDataBit(EPAPER_BUSY_GPIO, EPAPER_BUSY_PIN));

}

/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_WriteCMD(uint8_t Command)

{

    SPI_CSInternalSelected(SPI1, ENABLE);

    EPAPER_SPI_WriteData(Command & 0xFFFFFEFF);

    SPI_CSInternalSelected(SPI1, DISABLE);

}

/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_WriteDAT(uint8_t Data)

{

    SPI_CSInternalSelected(SPI1, ENABLE);

    EPAPER_SPI_WriteData(Data | 0x00000100);

    SPI_CSInternalSelected(SPI1, DISABLE);

}

演示实现代码:
复制
/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_Init(void)

{

    EPAPER_InitGPIO();

    EPAPER_InitSPI1();

    EPAPER_NORMAL_Init();

    EPAPER_NORMAL_Display(gImage_MM32);

    SysTick_DelayMS(1000);

    EPAPER_NORMAL_Init();

    EPAPER_WriteRAM_BaseMAP(gImage_LOGO);

    

    EPAPER_PARTIAL_Init();

    EPAPER_PARTIAL_Display(0, 32, 32, 32, gImage_NUM1, 1); SysTick_DelayMS(250);

    EPAPER_PARTIAL_Display(0, 32, 32, 32, gImage_NUM2, 1); SysTick_DelayMS(250);

    EPAPER_PARTIAL_Display(0, 32, 32, 32, gImage_NUM3, 1); SysTick_DelayMS(250);

    EPAPER_PARTIAL_Display(0, 32, 32, 32, gImage_NUM4, 1); SysTick_DelayMS(250);

    EPAPER_PARTIAL_Display(0, 32, 32, 32, gImage_NUM5, 1); SysTick_DelayMS(250);

    EPAPER_PARTIAL_Display(0, 32, 32, 32, gImage_NUM6, 1); SysTick_DelayMS(250);

    EPAPER_PARTIAL_Display(0, 32, 32, 32, gImage_NUM7, 1); SysTick_DelayMS(250);

    EPAPER_PARTIAL_Display(0, 32, 32, 32, gImage_NUM8, 1); SysTick_DelayMS(250);

    EPAPER_PARTIAL_Display(0, 32, 32, 32, gImage_NUM9, 1); SysTick_DelayMS(250);

    EPAPER_NORMAL_Init();

    EPAPER_WriteRAM_BaseMAP(gImage_CLOCK);

    EPAPER_PARTIAL_Init();

    for(uint32_t i = 0; i < 60 * 60; i++)

    {

        uint8_t Minute = i / 60;

        uint8_t Second = i % 60;

        EPAPER_PARTIAL_DisplayClock(32,  90, (const uint8_t *)&gImage_TIME[256 * (Second % 10)],

                                    32, 122, (const uint8_t *)&gImage_TIME[256 * (Second / 10)],

                                    24, 162, (const uint8_t *)&gImage_TIME[256 * 10],

                                    32, 204, (const uint8_t *)&gImage_TIME[256 * (Minute % 10)],

                                    32, 236, (const uint8_t *)&gImage_TIME[256 * (Minute / 10)], 32, 64);

        if((Minute == 0) && (Second == 18))

        {

            goto EPAPER_CLEAR;

        }

        SysTick_DelayMS(1000);

    }

EPAPER_CLEAR:

    EPAPER_NORMAL_Init();

    EPAPER_WriteRAM_BaseMAP(gImage_PANDA);

    EPAPER_PARTIAL_Init();

}

uint8_t EPAPER_SHELL_Index = 0;

/*******************************************************************************

 * @brief       

 * @param       

 * @retval      

 * @attention   

*******************************************************************************/

void EPAPER_Handler(void)

{

    const uint8_t *Image = NULL;

    if(EPAPER_SHELL_Index != 0)

    {

        switch(EPAPER_SHELL_Index)

        {

            case 1 : Image = gImage_1; break;

            case 2 : Image = gImage_2; break;

            case 3 : Image = gImage_3; break;

            case 4 : Image = gImage_4; break;

            case 5 : Image = gImage_5; break;

            case 6 : Image = gImage_6; break;

            case 7 : Image = gImage_7; break;

            case 8 : Image = gImage_8; break;

            case 9 : Image = gImage_9; break;

            case 10: Image = gImage_0; break;

            default:

                EPAPER_NORMAL_Init();

                EPAPER_NORMAL_Clear(0x00);

                EPAPER_NORMAL_Init();

                EPAPER_NORMAL_Clear(0xFF);

                SysTick_DelayMS(100);

                EPAPER_DeepSleep();

                break;

        }

        if(Image != NULL)

        {

            EPAPER_PARTIAL_Display(0, 48, 48, 48, Image, 1);

        }

        EPAPER_SHELL_Index = 0;

    }

}

运行效果:


对于显示资源不多的,可能MCU内部FLASH存储空间就足够了;对于一个电子小说,一个大容量的SPI FLASH可能也可以满足需求了;对于更丰富的应用,作为MM32F3270系列的MCU,有着FSMC接口控制器,可以扩展容量更大的NOR  FLASH,来实现应用资源的存储。
---------------------
作者:xld0932
链接:https://bbs.21ic.com/icview-3199536-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值