[PIC®/AVR®/dsPIC®产品]

[PIC18F57Q43Curiosity Nano] 3.软件模拟SPI驱动OLED

[复制链接]
24366|15
手机看帖
扫描二维码
随时随地手机跟帖
zhouminjie|  楼主 | 2022-8-22 23:19 | 显示全部楼层 |阅读模式
本帖最后由 zhouminjie 于 2022-8-22 23:21 编辑

本片介绍在PIC18F57Q43上实现软件模拟SPI驱动0.96OLED
新建工程,打开MCC配置
配置系统时钟频率:时钟源选择HFINTOSC,时钟频率选择64MHz,分频设置选择不分频,得到系统时钟频率为64MHz
截图04.png

配置引脚:在Package View中设置RA3——DC、RA4——CS、RB1——MISO、RB2——MOSI、RB3——SCK、RD4——RES
截图05.png

微信图片_20220822224019.jpg

添加DELAY Foundation services
截图06.png

点击Generate生成配置代码
截图07.png

新建.c(.h)文件,添加sw_spi代码
#include"sw_spi.h"

//SPI初始化
void SPI_Initial(void)
{
    SPI_CS_DISABLE;
    SPI_SCK_HIGH;
    SPI_MOSI_HIGH;
}

//SPI写一字节数据,TxData --- 发送的字节数据
void SPI_WriteByte(unsigned char TxData)
{
    unsigned char cnt;

    for(cnt = 0; cnt < 8; cnt++)
    {
        SPI_SCK_LOW;                                 //时钟 - 低
        Delay_Us(1);

        if(TxData & 0x80)                            //发送数据
            SPI_MOSI_HIGH;
        else
            SPI_MOSI_LOW;

        TxData <<= 1;

        Delay_Us(1);
        SPI_SCK_HIGH;                                //时钟 - 高
        Delay_Us(1);
    }
}

//SPI读一字节数据,读回来的字节数据
unsigned char SPI_ReadByte(void)
{
    unsigned char cnt;
    unsigned char RxData = 0;

    for(cnt = 0; cnt < 8; cnt++)
    {
        SPI_SCK_LOW;                                 //时钟 - 低
        Delay_Us(1);

        RxData <<= 1;

        if(SPI_MISO_READ)                            //读取数据
        {
            RxData |= 0x01;
        }

        SPI_SCK_HIGH;                                //时钟 - 高
        Delay_Us(1);
    }

    return RxData;
}


新建.c(.h)文件,添加oled代码
#include"oled.h"

//向设备写控制命令
static void OLED_Write_CMD(unsigned char cmd)
{
    OLED_CS_LOW;
    OLED_DC_LOW;
    SPI_WriteByte(cmd);
    OLED_CS_HIGH;
}

//向设备写数据
static void OLED_Write_Data(unsigned char data)
{
    OLED_CS_LOW;
    OLED_DC_HIGH;
    SPI_WriteByte(data);
    OLED_CS_HIGH;
}

//坐标设置
static void OLED_Set_Pos(unsigned char x, unsigned char y)
{
    OLED_Write_CMD(0xB0 + y);
    OLED_Write_CMD(((x & 0xF0) >> 4) | 0x10);
    OLED_Write_CMD(x & 0x0F);
}

//开启OLED显示
static void OLED_Display_On(void)
{
    OLED_Write_CMD(0x8D); //SET DCDC命令
    OLED_Write_CMD(0x14); //DCDC ON
    OLED_Write_CMD(0xAF); //DISPLAY ON
}

//关闭OLED显示
static void OLED_Display_Off(void)
{
    OLED_Write_CMD(0x8D); //SET DCDC命令
    OLED_Write_CMD(0x10); //DCDC OFF
    OLED_Write_CMD(0xAE); //DISPLAY OFF
}

//OLED清屏
void OLED_Clear(void)
{
    unsigned char cnt, count;

    for(cnt = 0; cnt < 8; cnt++)
    {
        OLED_Write_CMD(0xB0 + cnt);
        OLED_Write_CMD(0x00);
        OLED_Write_CMD(0x10);

        for(count = 0; count < 128; count++)
        {
            OLED_Write_Data(0x00);
        }
    }
}

//OLED清行
void OLED_Clear_Row(unsigned char n)
{
    unsigned char count;

    OLED_Write_CMD(0xB0 + n);
    OLED_Write_CMD(0x00);
    OLED_Write_CMD(0x10);

    for(count = 0; count < 128; count++)
    {
        OLED_Write_Data(0x00);
    }
}

//OLED填满屏幕
void OLED_Fill(void)
{
    unsigned char cnt, count;

    for(cnt = 0; cnt < 8; cnt++)
    {
        OLED_Write_CMD(0xB0 + cnt); //设置页地址(0~7)
        OLED_Write_CMD(0x00); //设置显示位置—列低地址
        OLED_Write_CMD(0x10); //设置显示位置—列高地址

        for(count = 0; count < 128; count++)
        {
            OLED_Write_Data(0x01);
        }
    }
}

//指定位置显示一个字符
//x:0~127
//y:0~63
//chr:字符
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char chr, unsigned char size)
{
    unsigned char offset = 0, cnt = 0;

    offset = chr - ' '; //计算偏移量

    if(x > 128 - 1)
    {
        x = 0;
        y = y + 2;
    }

    if(size == 16)
    {
        OLED_Set_Pos(x, y);

        for(cnt = 0; cnt < 8; cnt++)
        {
            OLED_Write_Data(F8x16[offset * 16 + cnt]);
        }

        OLED_Set_Pos(x, y + 1);

        for(cnt = 0; cnt < 8; cnt++)
        {
            OLED_Write_Data(F8x16[offset * 16 + cnt + 8]);
        }
    }
    else
    {
        OLED_Set_Pos(x, y);

        for(cnt = 0; cnt < 6; cnt++)
        {
            OLED_Write_Data(F6x8[offset][cnt]);
        }
    }
}

unsigned int oled_pow(unsigned char m, unsigned char n)
{
    unsigned int result = 1;

    while(n--)
    {
        result *= m;
    }

    return result;
}

//指定位置显示一个数字
//x,y:起点坐标
//num:数值(0~4294967295)
//len:数字的位数
//size:字体大小
//mode:模式        0,填充模式;1,叠加模式
void OLED_ShowNum(unsigned char x, unsigned char y, unsigned int num, unsigned char len, unsigned char size)
{
    unsigned char cnt, temp;
    unsigned char show = 0;

    for(cnt = 0; cnt < len; cnt++)
    {
        temp = (num / oled_pow(10, len - cnt - 1)) % 10;

        if(show == 0 && cnt < (len - 1))
        {
            if(temp == 0)
            {
                OLED_ShowChar(x + (size / 2) * cnt, y, ' ', size);
                continue;
            }
            else
            {
                show = 1;
            }
        }

        OLED_ShowChar(x + (size / 2) * cnt, y, temp + '0', size);
    }
}

//指定位置显示字符串
void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *chr, unsigned char size)
{
    unsigned char cnt = 0;

    while(chr[cnt] != '\0')
    {
        OLED_ShowChar(x, y, chr[cnt], size);
        x += 8;

        if(x > 120)
        {
            x = 0;
            y += 2;
        }

        cnt++;
    }
}

//显示汉字
void OLED_ShowCHinese(unsigned char x, unsigned char y, unsigned char no)
{
    unsigned char cnt, addr = 0;

    OLED_Set_Pos(x, y);

    for(cnt = 0; cnt < 16; cnt++)
    {
        OLED_Write_Data(Hzk[2 * no][cnt]);
        addr++;
    }

    OLED_Set_Pos(x, y + 1);

    for(cnt = 0; cnt < 16; cnt++)
    {
        OLED_Write_Data(Hzk[2 * no + 1][cnt]);
        addr++;
    }
}

//显示图片
/*
        [url=home.php?mod=space&uid=247401]@brief[/url]                        显示图片
        @param                        x0:起始列地址
                                        y0:起始页地址
                                        x1:终止列地址
                                        y1:终止页地址
                                        BMP[]:存放图片代码的数组
        @retval                        无
*/
void OLED_DrawBMP(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, const unsigned char BMP[])
{
    unsigned int j = 0; //定义变量
    unsigned char x, y; //定义变量

    if(y1 % 8 == 0)
    {
        y = y1 / 8; //判断终止页是否为8的整数倍
    }
    else
    {
        y = y1 / 8 + 1;
    }

    for(y = y0; y < y1; y++) //从起始页开始,画到终止页
    {
        OLED_Set_Pos(x0, y); //在页的起始列开始画

        for(x = x0; x < x1; x++) //画x1 - x0 列
        {
            OLED_Write_Data(BMP[j++]); //画图片的点
        }
    }
}

//显示动图
/*
        [url=home.php?mod=space&uid=247401]@brief[/url]                        显示动图
        @param                        x0:起始列地址
                                y0:起始页地址
                                x1:终止列地址
                                y1:终止页地址
                                k: 帧个数
                                m: 单帧数组大小
                                BMP[][m]:存放动图代码的数组
        @retval                        无
*/
void OLED_DrawGIF(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char k, int m, const unsigned char GIF[29][1024])
{
    unsigned int j = 0; //定义变量
    unsigned char x, y, i; //定义变量
    //unsigned int (*p)[m] = GIF;

    if(y1 % 8 == 0)
    {
        y = y1 / 8; //判断终止页是否为8的整数倍
    }
    else
    {
        y = y1 / 8 + 1;
    }

    for (i = 0; i < k; i++) //从第一帧开始画
    {
        j = 0;

        for(y = y0; y < y1; y++) //从起始页开始,画到终止页
        {
            OLED_Set_Pos(x0, y); //在页的起始列开始画

            for(x = x0; x < x1; x++) //画x1 - x0 列
            {
                OLED_Write_Data(GIF[i][j++]); //画图片的点//GIF[i][j++]
            }
        }

        //Delay_Ms(80);
    }
}

//SPI驱动方式DC、RES引脚初始化
void OLED_SPI_GPIO_Init(void)
{
    OLED_RES_LOW;
    DELAY_milliseconds(200);
    OLED_RES_HIGH;   
}
   
//OLED初始化
void OLED_Init(void)
{
    SPI_Initial();
    OLED_SPI_GPIO_Init();
   
    DELAY_milliseconds(200);
    OLED_Write_CMD(0xAE); //display off
    OLED_Write_CMD(0x00); //set low column address
    OLED_Write_CMD(0x10); //set high column address
    OLED_Write_CMD(0x40); //set start line address
    OLED_Write_CMD(0xB0); //set page address
    OLED_Write_CMD(0x81); //contract control
    OLED_Write_CMD(0xFF); //128
    OLED_Write_CMD(0xA1); //set segment remap
    OLED_Write_CMD(0xA6); //normal / reverse
    OLED_Write_CMD(0xA8); //set multiplex ratio(1 to 64)
    OLED_Write_CMD(0x3F); //1/32 duty
    OLED_Write_CMD(0xC8); //Com scan direction
    OLED_Write_CMD(0xD3); //set display offset
    OLED_Write_CMD(0x00); //
    OLED_Write_CMD(0xD5); //set osc division
    OLED_Write_CMD(0x80); //
    OLED_Write_CMD(0xD8); //set area color mode off
    OLED_Write_CMD(0x05); //
    OLED_Write_CMD(0xD9); //Set Pre-Charge Period
    OLED_Write_CMD(0xF1); //
    OLED_Write_CMD(0xDA); //set com pin configuartion
    OLED_Write_CMD(0x12); //
    OLED_Write_CMD(0xDB); //set Vcomh
    OLED_Write_CMD(0x30); //
    OLED_Write_CMD(0x8D); //set charge pump enable
    OLED_Write_CMD(0x14); //
    OLED_Write_CMD(0xAF); //turn on oled panel
}


编译调试
测试效果如下
WeChat_20220822224134_ (1) (1).gif

测试工程
PIC18F57_SW_SPI.X.zip (1.21 MB)

使用特权

评论回复
bestwell| | 2022-9-3 16:40 | 显示全部楼层
用I/O口使用软件模拟SPI通信怎么模拟

使用特权

评论回复
fentianyou| | 2022-9-3 18:34 | 显示全部楼层
怎么用spi接口控制12864屏

使用特权

评论回复
wilhelmina2| | 2022-9-3 18:54 | 显示全部楼层
6和7脚OLED的SPI驱动程序一样吗  

使用特权

评论回复
tifmill| | 2022-9-3 19:26 | 显示全部楼层
单片机驱动oled简单吗

使用特权

评论回复
cashrwood| | 2022-9-3 20:39 | 显示全部楼层
可以运行spi接口的ssd1306d的例程吗?

使用特权

评论回复
pklong| | 2022-9-4 22:07 | 显示全部楼层
SPI驱动OLED 12864   

使用特权

评论回复
vivilyly| | 2022-9-5 12:57 | 显示全部楼层
可以通过模拟spi来驱动吗  

使用特权

评论回复
mickit| | 2022-9-5 14:56 | 显示全部楼层
怎么提高刷屏速度  

使用特权

评论回复
averyleigh| | 2022-9-5 16:06 | 显示全部楼层
LED和OLED有什么区别?

使用特权

评论回复
tpgf| | 2022-9-7 14:19 | 显示全部楼层
模拟的几线制的spi啊

使用特权

评论回复
nawu| | 2022-9-7 14:34 | 显示全部楼层
mickit 发表于 2022-9-5 14:56
怎么提高刷屏速度

刷屏速度太快了也不好啊

使用特权

评论回复
aoyi| | 2022-9-7 14:42 | 显示全部楼层
cashrwood 发表于 2022-9-3 20:39
可以运行spi接口的ssd1306d的例程吗?

这种接口的屏其数据接收量是有限制的吧

使用特权

评论回复
zljiu| | 2022-9-7 14:49 | 显示全部楼层
bestwell 发表于 2022-9-3 16:40
用I/O口使用软件模拟SPI通信怎么模拟

模拟其通讯的时序呀

使用特权

评论回复
gwsan| | 2022-9-7 14:57 | 显示全部楼层
模拟spi比模拟iic难一点

使用特权

评论回复
tfqi| | 2022-9-7 15:05 | 显示全部楼层
一帧一帧的刷新是手机录制导致的吗

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

31

主题

134

帖子

3

粉丝