打印
[综合信息]

HC32F4 硬件IIC接口驱动OLED

[复制链接]
538|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wiba|  楼主 | 2023-10-17 17:01 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
使用硬件IIC接口进行驱动,先上硬件IIC驱动代码;

/*************************************************************************************************************
* 文件名                :        hc32f46x_iic.c
* 功能                        :        hc32f46x芯片IIC主机通讯
* 作者                        :        cp1300@139.com
* 创建时间                :        2022-05-23
* 最后修改时间        :        2022-05-23
* 详细                        :       
*************************************************************************************************************/
#include "system.h"
#include "hc32f46x_iic.h"
#include "hc32f46x.h"
#include "typedef.h"
#include "hc32f46x_const.h"
#include "hc32f46x_map.h"
#include "stdio.h"

//IIC句柄
typedef struct
{
        IIC_CH_Type ch;                        //当前通道
        IIC_TypeDef* IICx;                //当前通道外设结构体
        u32 TimeOutUs;                        //操作超时,单位us
        u16 Speed_KHz;                        //通讯速度,单位KHz
        bool isMasterMode;                //是否为主设备模式-目前只支持主设备模式
}IIC_HANDLE;



//IIC通道句柄定义
static IIC_HANDLE sg_IIC_Handle[IIC_CH_COUNT];
//IIC外设结构指针
static const  IIC_TypeDef* const scg_IICx_Base[IIC_CH_COUNT] = { IIC1,IIC2,IIC3};


/*************************************************************************************************************************
* 函数                :        static IIC_ERROR IIC_WaitStatus(IIC_HANDLE* pHandle, u32 Status)
* 功能                :        等待IIC的某个状态有效
* 参数                :        pHandle:句柄
* 返回                :        IIC_ERROR
* 依赖                :        底层宏定义
* 作者                :        cp1300@139.com
* 时间                :        2022-01-18
* 最后修改时间:         2022-01-18
* 说明                :
*************************************************************************************************************************/
static IIC_ERROR IIC_WaitStatus(IIC_HANDLE* pHandle, u32 Status)
{
        u16 TimeOut = 100;

        //等待
        while (((pHandle->IICx->SR & Status)==0) && TimeOut)                        //等待状态有效
        {
                Delay_US(1);
                TimeOut--;
        }

        if (pHandle->IICx->SR & Status)                                                        //有效了
        {
                return IIC_OK;
        }
        else if (pHandle->IICx->SR & BIT9)                                                //仲裁失败-总线有问题
        {
                //uart_printf("等待状态0X%02X失败,发生了仲裁错误,SR:0x%X\r\n", Status, pHandle->IICx->SR);
                return IIC_HAL_ERROR;
        }
        else
        {
                //uart_printf("等待状态0X%02X失败,超时错误,SR:0x%X\r\n", Status, pHandle->IICx->SR);
                return IIC_TIMEOUT;
        }
}

//发送ACK
static void IIC_SendACK(IIC_HANDLE* pHandle)
{
        pHandle->IICx->CR1 &= ~BIT10;
}
//发送NAK
static void IIC_SendNAK(IIC_HANDLE *pHandle)
{
        pHandle->IICx->CR1 |= BIT10;
}

//发送STOP
static void IIC_SendStop(IIC_HANDLE* pHandle)
{
        pHandle->IICx->CLR = BIT4;        //清除STOP标志
        pHandle->IICx->CR1 |= BIT9;
        IIC_WaitStatus(pHandle, BIT4);                //等待状态有效
}
//发送START
static void IIC_SendStart(IIC_HANDLE *pHandle)
{
        pHandle->IICx->CLR = BIT0;        //清除START标志
        pHandle->IICx->CR1 |= BIT8;
        IIC_WaitStatus(pHandle, BIT0);                //等待状态有效
}
//发送重复START
static __inline void IIC_SendReStart(IIC_HANDLE* pHandle)
{
        pHandle->IICx->CLR = BIT0;        //清除START标志
        pHandle->IICx->CR1 |= BIT7;
        IIC_WaitStatus(pHandle, BIT0);                //等待状态有效
}


#define IIC_SR_NACK                        (1<<12)        //NACK
#define IIC_SR_ACK                        (1<<10)        //ACK
#define IIC_SR_BUSY                        (1<<17)        //总线忙
#define IIC_SR_AL                        (1<<9)        //仲裁丢失(只读)-总线电平故障
#define IIC_SR_TEMPTY                (1<<7)        //发送DTR寄存器为空
#define IIC_SR_RFULL                (1<<6)        //接收数据满标志
#define IIC_SR_STOP                        (1<<4)        //检测到停止
#define IIC_SR_TEND                        (1<<3)        //数据从缓冲器中发送完成了
#define IIC_SR_START                (1<<0)        //检测到开始条件

/*************************************************************************************************************************
* 函数        :                        bool IIC_Init(IIC_CH_Type ch, u16 Speed_KHz, u16 TimeOutUs)
* 功能        :                        硬件IIC初始化
* 参数        :                        ch:IIC通道;Speed_KHz:速度1-400;TimeOutUs:操作超时us(0:自定计算超时)
* 返回        :                        IIC_ERROR
* 依赖        :                        底层宏定义
* 作者        :                        cp1300@139.com
* 时间        :                        2022-01-18
* 最后修改时间 :                 2022-01-18
* 说明        :                         速度只是个大概的计算值,正常情况下只要时钟速度符合要求,
                                        时钟上升沿到来前数据以及稳定的切换了即可保证通讯稳定性
*************************************************************************************************************************/
bool IIC_Init(IIC_CH_Type ch, u16 Speed_KHz, u16 TimeOutUs)
{
        IIC_HANDLE *pHandle;
        u32 temp;
        u32 clock;
       
        switch (ch)
        {
                case IIC_CH1:        //IIC1
                {
                        SYS_DeviceClockEnable(DEV_IIC1, TRUE);
                }break;
                case IIC_CH2:        //IIC2
                {
                        SYS_DeviceClockEnable(DEV_IIC2, TRUE);
                }break;
                case IIC_CH3:        //IIC3
                {
                        SYS_DeviceClockEnable(DEV_IIC3, TRUE);
                }break;
                default:
                {
                        DEBUG("初始化IIC失败:无效的IIC通道%d\r\n", ch);
                        return FALSE;
                }
        }
        pHandle = &sg_IIC_Handle[ch];                                                                        //获取相关通道的句柄
        pHandle->IICx = (IIC_TypeDef *)scg_IICx_Base[ch];                                //外设指针
        if(Speed_KHz > 400) Speed_KHz = 400;
        if(Speed_KHz < 1) Speed_KHz = 1;
        pHandle->Speed_KHz = Speed_KHz;                                                                        //记录速度
        if(TimeOutUs == 0)                                                                                                //需要自动计算超时时间,时钟周期*10*10
        {
                TimeOutUs = 1000/Speed_KHz;                                                                        //时钟周期
                TimeOutUs *= 10;                                                                                        //字节周期
                TimeOutUs *= 10;                                                                                        //超时时间为10个字节时间
        }
        if(TimeOutUs < 3) TimeOutUs = 3;
        pHandle->TimeOutUs = TimeOutUs;                                                                        //记录通讯超时时间
        //uart_printf("IIC超时时间:%dus\r\n", pHandle->TimeOutUs);
       
       
        pHandle->IICx->CR1 = BIT15;                                                                //寄存器复位
        nop; nop;
        pHandle->IICx->CR1 = BIT15|BIT0;                                                //使能IIC功能,并将IIC逻辑处于复位状态
        nop; nop;
        pHandle->IICx->CR2 = 0;                                                                        //关闭所有中断
        pHandle->IICx->CR3 = BIT7;                                                                //关闭电平超时检查
        pHandle->IICx->FLTR = BIT5;                                                                //只开启模拟滤波功能

        //设置时钟速度
        temp = SYS_GetPCLK3Speed()/1000000;                                                //得到PCLK3时钟速度,时钟控制在2-4MHz
        pHandle->IICx->CCR &= ~((0x03 << 16) | (0x1F << 8) | (0x1F << 0));        //清除之前设置
        if(temp >= 32)
        {
                pHandle->IICx->CCR |= 4 << 16;        //16分频
                temp *= 1000;
                temp /= 16;
        }
        else if(temp >= 16)
        {
                pHandle->IICx->CCR |= 3 << 16;        //8分频
                temp *= 1000;
                temp /= 8;
        }
        else if(temp >= 8)
        {
                pHandle->IICx->CCR |= 2 << 16;        //4分频
                temp *= 1000;
                temp /= 4;
        }
        else if(temp >= 4)
        {
                pHandle->IICx->CCR |= 1 << 16;        //2分频
                temp *= 1000;
                temp /= 2;
        }
        else
        {
                temp *= 1000;
                temp /= 1;
        }
        clock = temp;        //IIC基准时钟
        //计算最小时钟
        temp = clock/(0x1F * 2 +6);
        if(Speed_KHz < temp) Speed_KHz = temp;
        temp = (clock / Speed_KHz) + 3;
        temp /= 2;
        if(temp < 3) temp = 0;
        else temp -= 3;
        pHandle->IICx->CCR |= temp << 8;        //设定高电平时间
        pHandle->IICx->CCR |= temp << 0;        //设定低电平时间
        //计算实际时钟速度
        temp = clock / ((temp+3)*2);               
       
        INFO_S("IIC%d时钟速度:%dKhz\r\n", ch+1, temp);
       
        nop; nop;
        pHandle->IICx->CR1 = BIT0;                                                                //结束复位
        nop; nop;
       
        return TRUE;
}




/*************************************************************************************************************************
* 函数                :        static IIC_ERROR IIC_WaitIdle(IIC_HANDLE *pHandle)
* 功能                :        等待IIC空闲
* 参数                :        pHandle:句柄
* 返回                :        IIC_ERROR
* 依赖                :        底层宏定义
* 作者                :        cp1300@139.com
* 时间                :        2022-01-18
* 最后修改时间:         2022-01-18
* 说明                :        
*************************************************************************************************************************/
static IIC_ERROR IIC_WaitIdle(IIC_HANDLE* pHandle)
{
        u16 TimeOut = 10;

        //uart_printf("IIC SR:0x%X\r\n", pHandle->IICx->SR);
        if ((pHandle->IICx->SR & IIC_SR_AL) == 0) return IIC_OK;                //总线空闲
        else //总线忙
        {
                pHandle->IICx->CR1 |= BIT15;        //复位
                Delay_US(1);
                pHandle->IICx->CR1 &= ~BIT15;        //结束复位
                SYS_DelayMS(1);
        }
       
        if (pHandle->IICx->SR & IIC_SR_AL)                                                        //总线异常
        {
                DEBUG("IIC错误:复位后总线依旧错误 0x%X\r\n", pHandle->IICx->SR);
                return IIC_HAL_ERROR;
        }
        else return IIC_OK;
}





/*************************************************************************************************************************
* 函数                :        static __inline IIC_ERROR IIC_WaitSendEnd(IIC_HANDLE* pHandle)
* 功能                :        等待发送结束(用于最后一个字节发送完成后等待数据发送完成)
* 参数                :        pHandle:句柄
* 返回                :        IIC_ERROR
* 依赖                :        底层宏定义
* 作者                :        cp1300@139.com
* 时间                :        2022-01-18
* 最后修改时间:         2022-01-18
* 说明                :
*************************************************************************************************************************/
static __inline IIC_ERROR IIC_WaitSendEnd(IIC_HANDLE* pHandle)
{
        u16 TimeOut = pHandle->TimeOutUs;

        while (1)
        {
                Delay_US(1);
                if (pHandle->IICx->SR & IIC_SR_TEND) break;                        //传输完成
                //等待超时
                TimeOut--;
                if (TimeOut == 0) break;
        }

        if (pHandle->IICx->SR & IIC_SR_TEND)                                        //发送完成了
        {
                pHandle->IICx->CLR |= IIC_SR_TEND;                                        //清除传输完成状态
                if (pHandle->IICx->SR & IIC_SR_ACK)
                {
                        return IIC_OK;
                }
                else if (pHandle->IICx->SR & IIC_SR_NACK)
                {
                        return IIC_NACK;
                }
                else
                {
                        uart_printf("pHandle->IICx->SR=0x%X\r\n", pHandle->IICx->SR);

                        return IIC_HAL_ERROR;
                }
        }
        else
        {
               
                return IIC_TIMEOUT;
        }
}



/*************************************************************************************************************************
* 函数                :        static __inline IIC_ERROR IIC_WaitToSend(IIC_HANDLE* pHandle)
* 功能                :        等待可以发送(用于连续发送的时候,数据到发送移位寄存器后,发送寄存器为空,可以写入数据)
* 参数                :        pHandle:句柄
* 返回                :        IIC_ERROR
* 依赖                :        底层宏定义
* 作者                :        cp1300@139.com
* 时间                :        2022-01-18
* 最后修改时间:         2022-01-18
* 说明                :
*************************************************************************************************************************/
/*static __inline IIC_ERROR IIC_WaitToSend(IIC_HANDLE* pHandle)
{
        u16 TimeOut = pHandle->TimeOutUs;
        while (1)
        {
                Delay_US(1);
                if (pHandle->IICx->SR & IIC_SR_TEMPTY) break;                //发送寄存器为空,可以发送
                //等待超时
                TimeOut--;
                if (TimeOut == 0) break;
        }
        if (pHandle->IICx->SR & IIC_SR_TEMPTY)                                        //发送为空,可以发送
        {
                return IIC_OK;
        }
        else
        {
                return IIC_TIMEOUT;
        }
}*/


/*************************************************************************************************************************
* 函数                :        static __inline IIC_ERROR IIC_WaitSendComplete(IIC_HANDLE* pHandle)
* 功能                :        等待发送完成
* 参数                :        pHandle:句柄
* 返回                :        IIC_ERROR
* 依赖                :        底层宏定义
* 作者                :        cp1300@139.com
* 时间                :        2022-01-18
* 最后修改时间:         2022-01-18
* 说明                :
*************************************************************************************************************************/
static __inline IIC_ERROR IIC_WaitSendComplete(IIC_HANDLE* pHandle)
{
        u16 TimeOut = pHandle->TimeOutUs;

        while (1)
        {
                Delay_US(1);
                if (pHandle->IICx->SR & IIC_SR_TEND) break;                        //数据发送完成
                //等待超时
                TimeOut--;
                if (TimeOut == 0) break;
        }

        if (pHandle->IICx->SR & IIC_SR_TEND)                                        //数据发送完成
        {
                return IIC_OK;
        }
        else
        {

                return IIC_TIMEOUT;
        }
}


/*************************************************************************************************************************
* 函数                :        static __inline IIC_ERROR IIC_WaitReadComplete(IIC_HANDLE* pHandle)
* 功能                :        等待接收数据完成(可以读取接收到的数据)
* 参数                :        pHandle:句柄
* 返回                :        IIC_ERROR
* 依赖                :        底层宏定义
* 作者                :        cp1300@139.com
* 时间                :        2022-01-18
* 最后修改时间:         2022-01-18
* 说明                :
*************************************************************************************************************************/
static __inline IIC_ERROR IIC_WaitReadComplete(IIC_HANDLE* pHandle)
{
        u16 TimeOut = pHandle->TimeOutUs;

        while (1)
        {
                Delay_US(1);
                if (pHandle->IICx->SR & IIC_SR_RFULL) break;                        //传输完成
                //等待超时
                TimeOut--;
                if (TimeOut == 0) break;
        }

        if (pHandle->IICx->SR & IIC_SR_RFULL)
        {
                return IIC_OK;
        }
        else return IIC_TIMEOUT;
}



/*************************************************************************************************************************
* 函数                :        static __inline IIC_ERROR IIC_SendByte(IIC_HANDLE* pHandle, u8 data, bool isWaitSendComp)
* 功能                :        发送一字节数据(会等待发送寄存器是否为空,并不会等待发送完成)
* 参数                :        pHandle:句柄;data:要发送的数据;isWaitSendComp:是否等待发送完成(重复开始信号发出后的读取命令无需等待发送完成)
* 返回                :        IIC_ERROR
* 依赖                :        底层宏定义
* 作者                :        cp1300@139.com
* 时间                :        2022-01-18
* 最后修改时间:         2022-01-18
* 说明                :
*************************************************************************************************************************/
static __inline IIC_ERROR IIC_SendByte(IIC_HANDLE* pHandle, u8 data, bool isWaitSendComp)
{
        IIC_ERROR error;

        pHandle->IICx->CLR = IIC_SR_TEND;                                                //清除发送完成标志
        pHandle->IICx->DTR = data;                                                                //发送数据
        if (isWaitSendComp)
        {
                error = IIC_WaitSendComplete(pHandle);                                        //等待发送完成
        }
        else //无需等待,检查是否有错误
        {
                if (pHandle->IICx->SR & IIC_SR_AL)                                                        //总线异常
                {
                        error = IIC_HAL_ERROR;
                }
                else
                {
                        error = IIC_OK;
                }
        }
       
        return error;
}



/*************************************************************************************************************************
* 函数        :                        IIC_ERROR IIC_MasterReadReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr,
                                                u8 *pDataBuff, u16 ReadByteNum)
* 功能        :                        IIC读取寄存器(可以读取1个或者多个寄存器)
* 参数        :                        ch:IIC通道,见IIC_CH_Type;SlaveAddr:从机地址;RegAddr:要读取的寄存器地址;is8bitRegAddr:TRUE:8BIT寄存器地址,FALSE:16bit寄存器地址;
                                                pDataBuff:接收的字节数据缓冲区;ReadByteNum:要读取的寄存器数量;
* 返回        :                        IIC_ERROR
* 依赖        :                        底层宏定义
* 作者        :                        cp1300@139.com
* 时间        :                        2020-11-03
* 最后修改时间 :         2020-11-03
* 说明        :                         读取的数据都是小端模式,如果是16bit的寄存器,请读取偶数个数据,并且需要另外进行高低字节对调最后组成16bit数据
                                        可能的意外:比如一个异常的操作可能会导致IIC一直忙,此处检测到忙后会直接复位IIC,但是必须在应用层做好重入保护,比如
                                                增加信号量
                                        通过测试发现:在函数进入的时候,清除掉中断状态,之后几乎检测不到IIC忙的情况,当然在结束的时候如果检测到忙会主动发送一个STOP,在
                                                操作失败的情况下,会对IIC进行软复位,确保本次的错误不会影响下一次的IIC操作。
*************************************************************************************************************************/
IIC_ERROR IIC_MasterReadReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, u8* pDataBuff, u16 ReadByteNum)
{
        IIC_ERROR Error = IIC_OK;
        IIC_HANDLE* pHandle;
        u16 i;

        if (ch > (IIC_CH_COUNT - 1) || pDataBuff == NULL || ReadByteNum == 0)
        {
                DEBUG("IIC错误:无效的参数\r\n");
                return IIC_PARAMETER_ERROR;
        }
        pHandle = &sg_IIC_Handle[ch];                                        //获取相关通道的句柄

        Error = IIC_WaitIdle(pHandle);                                        //等待总线空闲
        if (Error != IIC_OK)                                                        //忙
        {
                DEBUG("IIC错误:总线忙,复位后未恢复\r\n");
                Error = IIC_BUSY;
                goto end_loop;
        }
        //清空所有状态
        pHandle->IICx->CLR = pHandle->IICx->SR;
        //发送START
        IIC_SendStart(pHandle);
        //发送地址以及写
        Error = IIC_SendByte(pHandle, SlaveAddr, TRUE);//发送写信息与开始信号,从机地址最低位为0,代表写
        if (IIC_OK != Error) goto end_loop;
        //发送寄存器地址
        if (is8bitRegAddr == FALSE)        //16bit寄存器地址,先发送高位
        {
                Error = IIC_SendByte(pHandle, RegAddr >> 8, TRUE);                        //寄存器地址高位
                if (IIC_OK != Error) goto end_loop;
        }
        Error = IIC_SendByte(pHandle, RegAddr, TRUE);                                                //寄存器地址低位
        if (IIC_OK != Error) goto end_loop;
        //发送从机地址及读信号
        IIC_SendReStart(pHandle);                                                                        //发送重复START
        Error = IIC_SendByte(pHandle, SlaveAddr | BIT0, FALSE);                        //发送写信息与开始信号,从机地址最低位为1,代表读取,此处不用等待发送完成,后续会读取数据
        if (IIC_OK != Error) goto end_loop;
        //循环读取数据
       
        for (i = 0; i < ReadByteNum; i++)
        {
                Error = IIC_WaitReadComplete(pHandle);                                        //等待读取数据
                if (IIC_OK != Error) goto end_loop;
                pDataBuff = pHandle->IICx->DRR;                                                //读取一字节数据

                if (i == (ReadByteNum - 1))        //已经是最后一字节了
                {
                        IIC_SendNAK(pHandle);                        //最后一字节,发送NAK
                }
                else
                {
                        IIC_SendACK(pHandle);                                        //读取后自动发送ACK
                }


        }


end_loop:
        //发送结束信号
        IIC_SendStop(pHandle);                                                                                //发送结束

        return Error;
}


/*************************************************************************************************************************
* 函数        :                        IIC_ERROR IIC_MasterWriteReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr,
                                                u8 *pDataBuff, u16 WriteByteNum)
* 功能        :                        IIC写寄存器(可以写1个或者多个寄存器)
* 参数        :                        ch:IIC通道,见IIC_CH_Type;SlaveAddr:从机地址;RegAddr:要写入的寄存器地址;is8bitRegAddr:TRUE:8BIT寄存器地址,FALSE:16bit寄存器地址;
                                                pDataBuff:写入的字节数据缓冲区;WriteByteNum:要写入的寄存器数量;
* 返回        :                        IIC_ERROR
* 依赖        :                        底层宏定义
* 作者        :                        cp1300@139.com
* 时间        :                        2020-11-03
* 最后修改时间 :         2020-11-03
* 说明        :                         写入的数据都是小端模式,如果是16bit的寄存器,请写入偶数个数据,并且需要提前进行高低字节对调最后组成高字节在前的数据buff
*************************************************************************************************************************/
IIC_ERROR IIC_MasterWriteReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, u8 *pDataBuff, u16 WriteByteNum)
{
        IIC_ERROR Error = IIC_OK;
        IIC_HANDLE *pHandle;
        u16 i;
       
        if(ch > (IIC_CH_COUNT-1) || pDataBuff == NULL || WriteByteNum == 0)
        {
                DEBUG("IIC错误:无效的参数\r\n");
                return IIC_PARAMETER_ERROR;
        }
        pHandle = &sg_IIC_Handle[ch];                                        //获取相关通道的句柄
       
        Error = IIC_WaitIdle(pHandle);                                        //等待总线空闲
        if(Error != IIC_OK)                                                                //忙
        {
                DEBUG("IIC错误:总线忙,复位后未恢复\r\n");
                Error = IIC_BUSY;
                goto end_loop;
        }
       
        //清空所有状态
        pHandle->IICx->CLR = pHandle->IICx->SR;
        //发送START
        IIC_SendStart(pHandle);
        //发送地址以及写
        Error = IIC_SendByte(pHandle, SlaveAddr, TRUE);                                                                //发送写信息与开始信号
        if(IIC_OK != Error) goto end_loop;
        //发送寄存器地址
        if(is8bitRegAddr == FALSE)        //16bit寄存器地址,先发送高位
        {
                Error = IIC_SendByte(pHandle, RegAddr>>8, TRUE);                        //寄存器地址高位
                if(IIC_OK != Error) goto end_loop;
        }
        Error = IIC_SendByte(pHandle, RegAddr, TRUE);                                        //寄存器地址低位
        if(IIC_OK != Error) goto end_loop;
        for(i = 0;i < WriteByteNum;i ++)
        {
                Error = IIC_SendByte(pHandle, pDataBuff, TRUE);                        //写入一字节
                if(IIC_OK != Error) goto end_loop;
        }
end_loop:
        //发送结束信号
        IIC_SendStop(pHandle);                                                                                //发送结束
       
        return Error;
}

/*************************************************************************************************************
* 文件名                :        hc32f46x_iic.c
* 功能                        :        hc32f46x芯片IIC主机通讯
* 作者                        :        cp1300@139.com
* 创建时间                :        2022-05-23
* 最后修改时间        :        2022-05-23
* 详细                        :
*************************************************************************************************************/
#ifndef __HC32F46X_IIC_H___
#define __HC32F46X_IIC_H___
#include "typedef.h"
#include "HC32F46X.h"
#include "HC32F46X_const.h"
#include "HC32F46X_map.h"

//IIC硬件接口选择
typedef enum
{
        IIC_CH1 = 0,        //IIC1
        IIC_CH2 = 1,        //IIC2
        IIC_CH3 = 2,        //IIC3
}IIC_CH_Type;
#define IIC_CH_COUNT        3        //3个IIC


//通讯错误状态
typedef enum
{
        IIC_OK = 0,        //没有错误
        IIC_PARAMETER_ERROR = 1,        //参数错误
        IIC_TIMEOUT = 2,        //超时错误,也可能是底层错误
        IIC_HAL_ERROR = 3,        //底层错误
        IIC_STOP_ERROR = 4,        //等待结束错误
        IIC_BUSY = 5,        //硬件忙
        IIC_NACK = 6,        //收到NACK了
}IIC_ERROR;


bool IIC_Init(IIC_CH_Type ch, u16 Speed_KHz, u16 TimeOutUs);//硬件IIC初始化
IIC_ERROR IIC_MasterReadReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, u8* pDataBuff, u16 ReadByteNum);        //IIC读取寄存器(可以读取1个或者多个寄存器)
IIC_ERROR IIC_MasterWriteReg(IIC_CH_Type ch, u16 SlaveAddr, u16 RegAddr, bool is8bitRegAddr, u8 *pDataBuff, u16 WriteByteNum);        //IIC写寄存器(可以写1个或者多个寄存器)

#endif /* __HC32F46X_IIC_H___ */

测试代码

IIC_Init(IIC_CH1, 400, 100);//硬件IIC初始化
SYS_GPIOx_SetAF(GPIOE, 0, AF_FG1_I2C1_SCL);
SYS_GPIOx_SetAF(GPIOE, 1, AF_FG1_I2C1_SDA);

error = IIC_MasterReadReg(IIC_CH1, IO_EXPAND_ADDR+0, 0x10, TRUE, RegData, 2);
                uart_printf("error=%d\t", error);
                uart_printf("data[0]=0x%X\r\n", RegData[0]);
OLED采用的SSD1306主控,下面实现底层接口,主要是IIC接口支持,与硬件相关接口

/*************************************************************************************************************
* 文件名:                        OLED_SSD1306_IF.c
* 功能:                        OLED相关接口
* 作者:                        cp1300@139.com
* 创建时间:                2022-10-19
* 最后修改时间:        2022-10-19
* 详细:                       
*************************************************************************************************************/
#include "system.h"
#include "hc32f46x_system.h"
#include "OLED_SSD1306_IF.h"
#include "SoftwareIIC.h"
#include "board.h"
#include "main.h"
#include "OLED_SSD1306.h"
#include "hc32f46x_iic.h"

SSD1306_HANDLE g_OLED_Handle;


//写寄存器索引
static void SSD1306_WriteRegIndex(u8 data)
{
        IIC_MasterWriteReg(OLED_IIC_CH, OLED_ADDR, 0x00, TRUE, &data, 1);        //写入1字节

}

//写字节数据
static void SSD1306_WriteByteData(u8 data)
{       
    IIC_MasterWriteReg(OLED_IIC_CH, OLED_ADDR, 0x40, TRUE, &data, 1);        //写入1字节
}

//写入多个字节数据
static void SSD1306_WriteData(u8 *pData, u16 ByteCount)
{
        IIC_MasterWriteReg(OLED_IIC_CH, OLED_ADDR, 0x40, TRUE, pData, ByteCount);        //写入多个字节
}




//显示UI初始化
void LCD_UI_Init(void)
{
//        u32 ms;
       
        OLED_IO_Init();                                                                     //底层IO初始化
        IIC_Init(OLED_IIC_CH, 400, 100);                                                                //硬件IIC初始化
       
        SYS_DelayMS(2);

        g_OLED_Handle.WriteRegIndex = SSD1306_WriteRegIndex;                //接口初始化:写寄存器索引
        g_OLED_Handle.WriteByteData = SSD1306_WriteByteData;                //接口初始化:写8bit数据
        g_OLED_Handle.WriteData = SSD1306_WriteData;                                        //写批量字节数据       
        SSD1306_Init(&g_OLED_Handle);       
        //SSD1306_Rotate180(&g_OLED_Handle, TRUE);                                                //旋转180度显示

       
    SSD1306_GRAM_ShowString(&g_OLED_Handle, 0, 0, "Welcome!1234567", FONT16_DEFAULT);
    SSD1306_GRAM_Up(&g_OLED_Handle, 0, 0, 16*8-1, 16-1);//更新显存到屏幕
//SSD1306_GRAM_UpAll(&g_OLED_Handle);
  //SSD1306_ReverseColor(&g_OLED_Handle, TRUE);        //设置反色显示
}



/*************************************************************************************************************
* 文件名:                        OLED_SSD1306_IF.h
* 功能:                        OLED相关接口
* 作者:                        cp1300@139.com
* 创建时间:                2022-10-19
* 最后修改时间:        2022-10-19
* 详细:                       
*************************************************************************************************************/
#ifndef _OLED_SSD1306_IF_
#define _OLED_SSD1306_IF_
#include "OLED_SSD1306.h"


extern SSD1306_HANDLE g_OLED_Handle;

//显示UI初始化
void LCD_UI_Init(void);

#endif //_OLED_SSD1306_IF_



//OLED应用层驱动代码,与底层硬件无关代码,使用了虚拟显存方式,通过显存操作比较方便,可以任意位置画点花线,然后批量更新;

/*************************************************************************************************************
* 文件名                :        OLED_SSD1306.c
* 功能                        :        OLED_SSD1306 底层驱动
* 作者                        :        cp1300@139.com
* 创建时间                :        2022-10-19
* 最后修改时间        :        2022-10-19
* 详细:                        OLED刷新方式,列行刷新,一次刷新一列的8bit,然后刷新下一列
*************************************************************************************************************/       
#include "system.h"
#include "OLED_SSD1306.h"
#include "board.h"
#if(LCD_ASCII_12X24_EN) //12X24大小字符显示
#include "ASCII_12x24.h"
#endif //LCD_ASCII_12X24_EN

#if(OLED_SSD1306_ENABLE) //使能
#include "ascii_8x16.h"

//汉字支持
#define CHINESE_ENABLE                0

#if        CHINESE_ENABLE
#include "fontlib.h"
#endif       


/*************************************************************************************************************************
* 函数                        :        void SSD1306_ReverseColor(SSD1306_HANDLE *pHandle, bool isReverseColor)
* 功能                        :        设置反色显示
* 参数                        :        pHandle:句柄;isReverseColor:TRUE:反色显示;FALSE:非反色显示
* 返回                        :        无
* 依赖                        :        底层函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间         :         2022-10-19
* 说明                        :        
*************************************************************************************************************************/
void SSD1306_ReverseColor(SSD1306_HANDLE *pHandle, bool isReverseColor)
{
        if(isReverseColor == TRUE)        //需要反色显示
        {
                pHandle->WriteRegIndex(0xA7);//反色显示
        }
        else
        {
                pHandle->WriteRegIndex(0xA6);//正常显示
        }       
}


/*************************************************************************************************************************
* 函数                        :        void SSD1306_Rotate180(SSD1306_HANDLE *pHandle, bool isRotate)
* 功能                        :        旋转180度显示
* 参数                        :        pHandle:句柄;isRotate:TRUE:旋转;FALSE:不旋转
* 返回                        :        无
* 依赖                        :        底层函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间         :         2022-10-19
* 说明                        :        
*************************************************************************************************************************/
void SSD1306_Rotate180(SSD1306_HANDLE *pHandle, bool isRotate)
{
        if(isRotate == TRUE)        //需要旋转180度
        {
                pHandle->WriteRegIndex(0xC0);//旋转
                pHandle->WriteRegIndex(0xA0);
        }
        else
        {
                pHandle->WriteRegIndex(0xC8);//正常显示
                pHandle->WriteRegIndex(0xA1);
        }       
}


/*************************************************************************************************************************
* 函数                        :        void SSD1306_EnableDisplay(SSD1306_HANDLE *pHandle, bool isEnable)
* 功能                        :        是否使能显示
* 参数                        :        pHandle:句柄;isRotate:TRUE:使能显示;FALSE:不显示
* 依赖                        :        底层函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间         :         2022-10-19
* 说明                        :        
*************************************************************************************************************************/
void SSD1306_EnableDisplay(SSD1306_HANDLE *pHandle, bool isEnable)
{
        if(isEnable == TRUE)        //需要显示
        {
                pHandle->WriteRegIndex(0x8D);        //电荷泵使能
                pHandle->WriteRegIndex(0x14);        //开启电荷泵
                pHandle->WriteRegIndex(0xAF);        //点亮屏幕
        }
        else
        {
                pHandle->WriteRegIndex(0x8D);        //电荷泵使能
                pHandle->WriteRegIndex(0x10);        //关闭电荷泵
                pHandle->WriteRegIndex(0xAE);        //关闭屏幕
        }       
}


/*************************************************************************************************************************
* 函数                        :        void SSD1306_Clear(SSD1306_HANDLE *pHandle)
* 功能                        :        清屏
* 参数                        :        pHandle:句柄;
* 依赖                        :        底层函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间         :         2022-10-19
* 说明                        :        
*************************************************************************************************************************/
void SSD1306_Clear(SSD1306_HANDLE *pHandle)
{
        u8 i,j;
       
        for(i = 0; i < 8; i++)
        {
                pHandle->WriteRegIndex(0xb0+i); //设置行起始地址
                pHandle->WriteRegIndex(0x00);   //设置低列起始地址
                pHandle->WriteRegIndex(0x10);   //设置高列起始地址
                for(j = 0; j < 128; j ++)
                {                       
                        pHandle->WriteByteData(0x00);
                }
        }
}

/*************************************************************************************************************************
* 函数                        :        void SSD1306_Fill(SSD1306_HANDLE *pHandle)
* 功能                        :        填充
* 参数                        :        pHandle:句柄;
* 依赖                        :        底层函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间         :         2022-10-19
* 说明                        :        
*************************************************************************************************************************/
void SSD1306_Fill(SSD1306_HANDLE *pHandle)
{
        u8 i,j;
       
        for(i = 0; i < 8; i++)
        {
                pHandle->WriteRegIndex(0xb0+i); //设置行起始地址
                pHandle->WriteRegIndex(0x00);   //设置低列起始地址
                pHandle->WriteRegIndex(0x10);   //设置高列起始地址
                for(j = 0; j < 128; j ++)
                {                       
                        pHandle->WriteByteData(0xFF);
                }
        }
}

/*************************************************************************************************************************
* 函数                        :        void SSD1306_Init(SSD1306_HANDLE *pHandle)
* 功能                        :        初始化
* 参数                        :        pHandle:句柄;
* 依赖                        :        底层函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间         :         2022-10-19
* 说明                        :        
*************************************************************************************************************************/
void SSD1306_Init(SSD1306_HANDLE *pHandle)
{
        pHandle->Width = 128;
        pHandle->Height = 64;
        SYS_DelayMS(100);
       
        pHandle->WriteRegIndex(0xAE);//--turn off oled panel
        pHandle->WriteRegIndex(0x00);//---set low column address
        pHandle->WriteRegIndex(0x10);//---set high column address
        pHandle->WriteRegIndex(0x40);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
        pHandle->WriteRegIndex(0x81);//--set contrast control register
        pHandle->WriteRegIndex(0xCF);// Set SEG Output Current Brightness
        pHandle->WriteRegIndex(0xA1);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
        pHandle->WriteRegIndex(0xC8);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
        pHandle->WriteRegIndex(0xA6);//--set normal display
        pHandle->WriteRegIndex(0xA8);//--set multiplex ratio(1 to 64)
        pHandle->WriteRegIndex(0x3f);//--1/64 duty
        pHandle->WriteRegIndex(0xD3);//-set display offset        Shift Mapping RAM Counter (0x00~0x3F)
        pHandle->WriteRegIndex(0x00);//-not offset
        pHandle->WriteRegIndex(0xd5);//--set display clock divide ratio/oscillator frequency
        pHandle->WriteRegIndex(0x80);//--set divide ratio, Set Clock as 100 Frames/Sec
        pHandle->WriteRegIndex(0xD9);//--set pre-charge period
        pHandle->WriteRegIndex(0xF1);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
        pHandle->WriteRegIndex(0xDA);//--set com pins hardware configuration
        pHandle->WriteRegIndex(0x12);
        pHandle->WriteRegIndex(0xDB);//--set vcomh
        pHandle->WriteRegIndex(0x30);//Set VCOM Deselect Level
        pHandle->WriteRegIndex(0x20);//-Set Page Addressing Mode (0x00/0x01/0x02)
        pHandle->WriteRegIndex(0x02);//
        pHandle->WriteRegIndex(0x8D);//--set Charge Pump enable/disable
        pHandle->WriteRegIndex(0x14);//--set(0x10) disable
        //pHandle->WriteRegIndex(0xA4);// Disable Entire Display On (0xa4/0xa5)
        //pHandle->WriteRegIndex(0xA6);// Disable Inverse Display On (0xa6/a7)
        SSD1306_Clear(pHandle);
        pHandle->WriteRegIndex(0xAF);
       
        //SSD1306_Rotate180(pHandle, TRUE);                        //旋转180度
        SYS_DelayMS(3);
}

/*************************************************************************************************************************
* 函数                        :        void SSD1306_GRAM_DrawPoint(SSD1306_HANDLE* pHandle, u8 x, u8 y)
* 功能                        :        在显存里面指定位置画点
* 参数                        :        pHandle:句柄;x:X坐标,0-127; y:y坐标,0-63
* 依赖                        :        底层函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间         :         2022-10-19
* 说明                        :
*************************************************************************************************************************/
void SSD1306_GRAM_DrawPoint(SSD1306_HANDLE* pHandle, u8 x, u8 y)
{
        if (y > 127)
                return;
        pHandle->LCD_GRAM[y / 8][x] |= (1 << (y % 8));
}

/*************************************************************************************************************************
* 函数                        :        void SSD1306_GRAM_DrawPoint(SSD1306_HANDLE* pHandle, u8 x, u8 y)
* 功能                        :        擦除显存里面指定位置的点
* 参数                        :        pHandle:句柄;x:X坐标,0-127; y:y坐标,0-63
* 依赖                        :        底层函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间         :         2022-10-19
* 说明                        :
*************************************************************************************************************************/
void SSD1306_GRAM_ClearPoint(SSD1306_HANDLE* pHandle, u8 x, u8 y)
{
        if (y > 127)
                return;
        pHandle->LCD_GRAM[y / 8][x] &= ~(1 << (y % 8));
}

/*************************************************************************************************************************
* 函数                        :        void SSD1306_SetAddress(SSD1306_HANDLE* pHandle, u8 x1, u8 page1, u8 x2, u8 page2)
* 功能                        :        设置光标地址
* 参数                        :        pHandle:句柄;x1,page1:起始坐标;x2,page2:终点坐标
* 依赖                        :        底层函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间         :         2022-10-19
* 说明                        :        page:0-7;x:0-127
*************************************************************************************************************************/
void SSD1306_SetAddress(SSD1306_HANDLE* pHandle, u8 x1, u8 page1, u8 x2, u8 page2)
{
        if (x2 > 127) x2 = 127;
        if (page2 > 7) page2 = 7;

        pHandle->WriteRegIndex(0x21);         //设置列范围
        pHandle->WriteRegIndex(x1);                //开始
        pHandle->WriteRegIndex(x2);                //结束
        pHandle->WriteRegIndex(0x22);         //设置page范围
        pHandle->WriteRegIndex(page1);   //开始
        pHandle->WriteRegIndex(page2);   //结束

        pHandle->WriteRegIndex(0x20);         //设置内存地址模式
        pHandle->WriteRegIndex(0x00);         //行地址模式

}

/*************************************************************************************************************************
* 函数                        :        void SSD1306_GRAM_ClearAll(SSD1306_HANDLE* pHandle)
* 功能                        :        清除全部显存
* 参数                        :        pHandle:句柄;
* 依赖                        :        底层函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间         :         2022-10-19
* 说明                        :
*************************************************************************************************************************/
void SSD1306_GRAM_ClearAll(SSD1306_HANDLE* pHandle)
{
        u16 i, j;

        for (i = 0; i < 8; i++)
        {
                for (j = 0; j < 128; j++)
                {
                        pHandle->LCD_GRAM[j] = 0x00;
                }
        }
}



/*************************************************************************************************************************
* 函数                        :        void SSD1306_GRAM_Up(SSD1306_HANDLE* pHandle, u16 xStart, u16 yStart, u16 xEnd, u16 yEnd)
* 功能                        :        更新显存到屏幕
* 参数                        :        pHandle:句柄;x1,y1:起始坐标;x2,y2:终点坐标
* 依赖                        :        底层函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间         :         2022-10-19
* 说明                        :        y坐标会自动对齐到页,8行为一页,增加x轴最大值限制为127
*************************************************************************************************************************/
void SSD1306_GRAM_Up(SSD1306_HANDLE* pHandle, u16 xStart, u16 yStart, u16 xEnd, u16 yEnd)
{
        u8 i,j;
       
        yStart /= 8;        //计算页地址
        yEnd /= 8;
        if (yEnd % 8)        //有余数
        {
                yEnd++;
        }
        if (yEnd > 7) yEnd = 7;
        if(xEnd > 127) xEnd = 127;

        SSD1306_SetAddress(pHandle, xStart, yStart, xEnd, yEnd);        //设置显示范围       
        for (i = 0; i < (yEnd - yStart + 1); i++)
        {
                //pHandle->WriteRegIndex(0xb0 + i); //设置行起始地址
                //pHandle->WriteRegIndex(0x00);   //设置低列起始地址
                //pHandle->WriteRegIndex(0x10);   //设置高列起始地址
                /*for (j = 0; j < (xEnd - xStart + 1); j++)
                {
                        pHandle->WriteByteData(pHandle->LCD_GRAM[yStart + i][xStart + j]);
                }*/
                pHandle->WriteData(&pHandle->LCD_GRAM[yStart + i][xStart], xEnd - xStart + 1);
        }
}



/*************************************************************************************************************************
* 函数                        :        void SSD1306_GRAM_Fill(SSD1306_HANDLE* pHandle, u16 xStart, u16 yStart, u16 xEnd, u16 yEnd)
* 功能                        :        GRAM填充
* 参数                        :        pHandle:句柄;xStart,yStart:起始坐标;xEnd,yEnd:终点坐标
* 依赖                        :        底层函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间         :         2022-10-19
* 说明                        :       
*************************************************************************************************************************/
void SSD1306_GRAM_Fill(SSD1306_HANDLE* pHandle, u16 xStart, u16 yStart, u16 xEnd, u16 yEnd)
{
        u16 i, j;

        for (i = xStart; i < (xEnd + 1); i++)
        {
                for (j = yStart; j < (yEnd + 1); j++)
                {
                        SSD1306_GRAM_DrawPoint(pHandle, i, j);
                }
        }
}


/*************************************************************************************************************************
* 函数                        :        void SSD1306_GRAM_Clear(SSD1306_HANDLE* pHandle, u16 xStart, u16 yStart, u16 xEnd, u16 yEnd)
* 功能                        :        GRAM清除
* 参数                        :        pHandle:句柄;xStart,yStart:起始坐标;xEnd,yEnd:终点坐标
* 依赖                        :        底层函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间         :         2022-10-19
* 说明                        :
*************************************************************************************************************************/
void SSD1306_GRAM_Clear(SSD1306_HANDLE* pHandle, u16 xStart, u16 yStart, u16 xEnd, u16 yEnd)
{
        u16 i, j;

        for (i = xStart; i < (xEnd + 1); i++)
        {
                for (j = yStart; j < (yEnd + 1); j++)
                {
                        SSD1306_GRAM_ClearPoint(pHandle, i, j);
                }
        }
}


/*************************************************************************************************************************
* 函数                        :        void SSD1306_GRAM_ShowChina(SSD1306_HANDLE* pHandle, u8 x, u8 y, const u8* p, FONT_MODE FontMode)
* 功能                        :        在指显存定位置显示一个指定大小的汉字
* 参数                        :        pHandle:句柄;x,y:显示开始坐标,p:汉字点阵缓冲区;
                                        FontMode:汉字显示模式,
                                        0x80:叠加显示,0x40:反显, 0:16号字体;1:12号字体
* 返回                        :        无
* 依赖                        :        画点函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间                :        2022-10-19
* 说明                        :         FontSize = 16或者 12
*************************************************************************************************************************/
void SSD1306_GRAM_ShowChina(SSD1306_HANDLE* pHandle, u8 x, u8 y, const u8* p, FONT_MODE FontMode)
{
        u8 i, j;
        u8 FontSize = (u8)FontMode & 0xff;        //获取字体大小
        void (*DrawPoint)(SSD1306_HANDLE* pHandle, u8 i, u8 j);
        void (*ClearPoint)(SSD1306_HANDLE* pHandle, u8 i, u8 j);


        if (FontMode & 0x4000)        //反显
        {
                DrawPoint = SSD1306_GRAM_ClearPoint;
                ClearPoint = SSD1306_GRAM_DrawPoint;
        }
        else //正常模式
        {
                ClearPoint = SSD1306_GRAM_ClearPoint;
                DrawPoint = SSD1306_GRAM_DrawPoint;
        }

        if (FontMode & 0x8000)        //叠加显示
        {
                for (i = 0; i < FontSize; i++)
                {
                        for (j = 0; j < 8; j++)
                        {
                                if (*p & 0x80 >> j)
                                        (*DrawPoint)(pHandle, x + j, y + i);
                        }
                        p++;
                        for (j = 0; j < FontSize - 8; j++)
                        {
                                if (*p & 0x80 >> j)
                                        (*DrawPoint)(pHandle, x + j + 8, y + i);
                        }
                        p++;
                }
        }
        else        //非叠加显示
        {
                for (i = 0; i < FontSize; i++)
                {
                        for (j = 0; j < 8; j++)
                        {
                                if (*p & 0x80 >> j)
                                        (*DrawPoint)(pHandle, x + j, y + i);
                                else
                                        (*ClearPoint)(pHandle, x + j, y + i);
                        }
                        p++;
                        for (j = 0; j < FontSize - 8; j++)
                        {
                                if (*p & 0x80 >> j)
                                        (*DrawPoint)(pHandle, x + j + 8, y + i);
                                else
                                        (*ClearPoint)(pHandle, x + j + 8, y + i);
                        }
                        p++;
                }
        }

}

/*************************************************************************************************************************
* 函数                        :        void SSD1306_GRAM_ShowChar(SSD1306_HANDLE* pHandle, u8 x, u8 y, u8 Char, FONT_MODE FontMode)
* 功能                        :        在显存指定位置显示一个指定大小的Char字符
* 参数                        :        pHandle:句柄;x,y:显示开始坐标;Char:要显示的1个字符;
                                        FontMode:汉字显示模式,
                                        0x80:叠加显示,0x40:反显, 0:16号字体;1:12号字体
* 返回                        :        无
* 依赖                        :        画点函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间                :        2022-10-19
* 说明                        :         FontSize = 16或者 12
*************************************************************************************************************************/
void SSD1306_GRAM_ShowChar(SSD1306_HANDLE* pHandle, u8 x, u8 y, u8 Char, FONT_MODE FontMode)
{
        u8 i, j, k;
        u8 FontSize = (u8)FontMode & 0xff;        //获取字体大小
        u8* p;
        u8 rows;                                                        //一个字符占用几个8行
        u8 column;                                                        //列数量
        u8 temp;
        void (*DrawPoint)(SSD1306_HANDLE* pHandle,u8 i, u8 j);
        void (*ClearPoint)(SSD1306_HANDLE* pHandle,u8 i, u8 j);

        Char -= 32;
        if (Char > ASCII_MAX - 1)
                return;



        if (FontSize == 12)
        {
                rows = 2;
                column = 8;
                p = (u8*)ASCII_8X12[Char];                //12号
        }
        else if (FontSize == 16)
        {
                rows = 2;
                column = 8;
                p = (u8*)ASCII_8X16[Char];                //16号
        }
#if(LCD_ASCII_12X24_EN) //12X24大小字符显示
        else if (FontSize == 24)
        {
                rows = 3;
                column = 12;
                p = (u8*)ASCII_12X24[Char];                //24号
        }
#endif //LCD_ASCII_12X24_EN       
        else
        {
                rows = 2;
                column = 8;
                p = (u8*)ASCII_8X12[Char];                //12号
        }

        if (FontMode & 0x4000)        //反显
        {
                DrawPoint = SSD1306_GRAM_ClearPoint;
                ClearPoint = SSD1306_GRAM_DrawPoint;
        }
        else //正常模式
        {
                ClearPoint = SSD1306_GRAM_ClearPoint;
                DrawPoint = SSD1306_GRAM_DrawPoint;
        }

        temp = 8;
        if (FontMode & 0x8000)        //叠加显示
        {
                for (k = 0; k < rows; k++)
                {
                        for (j = 0; j < column; j++)
                        {

                                for (i = 0; i < temp; i++)
                                {
                                        if (*p & (1 << i))
                                                (*DrawPoint)(pHandle, x + j, k * 8 + y + i);
                                }

                                p++;
                        }

                        FontSize -= 8;
                        if (FontSize >= 8) temp = 8;
                        else temp = FontSize;
                }

                /*for(j = 0;j < 8;j ++)
                {
                        for(i = 0;i < FontSize - 8;i ++)
                        {
                                if(*p & (1 << i))
                                        (*DrawPoint)(x + j,y + 8 + i);
                        }
                        p ++;
                }*/
        }
        else        //非叠加显示
        {

                for (k = 0; k < rows; k++)
                {
                        for (j = 0; j < column; j++)
                        {
                                for (i = 0; i < temp; i++)
                                {
                                        if (*p & (1 << i))
                                                (*DrawPoint)(pHandle, x + j, k * 8 + y + i);
                                        else
                                                (*ClearPoint)(pHandle, x + j, k * 8 + y + i);
                                }

                                p++;
                        }
                        FontSize -= 8;
                        if (FontSize >= 8) temp = 8;
                        else temp = FontSize;
                }


                /*
                for(j = 0;j < 8;j ++)
                {
                        for(i = 0;i < FontSize - 8;i ++)
                        {
                                if(*p & (1 << i))
                                        (*DrawPoint)(x + j,y + 8 + i);
                                else
                                        (*ClearPoint)(x + j,y + 8 + i);
                        }
                        p ++;
                }*/
        }
}

/*************************************************************************************************************************
* 函数                        :        void SSD1306_GRAM_DrawLine(SSD1306_HANDLE* pHandle, u16 x1, u16 y1, u16 x2, u16 y2)
* 功能                        :        在显存指定位置画线
* 参数                        :        pHandle:句柄;x1,y1:开始坐标;x2,y2:结束坐标                               
* 返回                        :        无
* 依赖                        :        画点函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间                :        2022-10-19
* 说明                        :        
*************************************************************************************************************************/
void SSD1306_GRAM_DrawLine(SSD1306_HANDLE* pHandle, u16 x1, u16 y1, u16 x2, u16 y2)
{
        u16 t;
        int xerr = 0, yerr = 0, delta_x, delta_y, distance;
        int incx, incy, uRow, uCol;

        delta_x = x2 - x1; //计算坐标增量
        delta_y = y2 - y1;
        uRow = x1;
        uCol = y1;
        if (delta_x > 0)incx = 1; //设置单步方向
        else if (delta_x == 0)incx = 0;//垂直线
        else { incx = -1; delta_x = -delta_x; }
        if (delta_y > 0)incy = 1;
        else if (delta_y == 0)incy = 0;//水平线
        else { incy = -1; delta_y = -delta_y; }
        if (delta_x > delta_y)distance = delta_x; //选取基本增量坐标轴
        else distance = delta_y;
        for (t = 0; t <= distance + 1; t++)//画线输出
        {
                SSD1306_GRAM_DrawPoint(pHandle, uRow, uCol);//画点
                xerr += delta_x;
                yerr += delta_y;
                if (xerr > distance)
                {
                        xerr -= distance;
                        uRow += incx;
                }
                if (yerr > distance)
                {
                        yerr -= distance;
                        uCol += incy;
                }
        }
}

/*************************************************************************************************************************
* 函数                        :        void SSD1306_GRAM_DrawRectangle(SSD1306_HANDLE* pHandle, u16 x1, u16 y1, u16 x2, u16 y2)
* 功能                        :        在显存指定位置画一个矩形
* 参数                        :        pHandle:句柄;x1,y1:开始坐标;x2,y2:结束坐标
* 返回                        :        无
* 依赖                        :        画点函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间                :        2022-10-19
* 说明                        :
*************************************************************************************************************************/
void SSD1306_GRAM_DrawRectangle(SSD1306_HANDLE* pHandle, u16 x1, u16 y1, u16 x2, u16 y2)
{
        SSD1306_GRAM_DrawLine(pHandle, x1, y1, x2, y1);
        SSD1306_GRAM_DrawLine(pHandle, x1, y1, x1, y2);
        SSD1306_GRAM_DrawLine(pHandle, x1, y2, x2, y2);
        SSD1306_GRAM_DrawLine(pHandle, x2, y1, x2, y2);
}


/*************************************************************************************************************************
* 函数                        :        void SSD1306_GRAM_ShowString(SSD1306_HANDLE* pHandle, u16 x, u16 y, const char* pStr, FONT_MODE FontMode)
* 功能                        :        在显存指定位置显示字符串
* 参数                        :        pHandle:句柄;x,y:显示开始坐标,pStr:字符串缓冲区;
                                        FontMode:显示模式,
                                                0x80:叠加显示,0x40:反显, 0:16号字体;1:12号字体
* 返回                        :        无
* 依赖                        :        画点函数
* 作者                        :        cp1300@139.com
* 时间                        :        2022-10-19
* 最后修改时间                :        2022-10-19
* 说明                        :
*************************************************************************************************************************/
void SSD1306_GRAM_ShowString(SSD1306_HANDLE* pHandle, u16 x, u16 y, const char* pStr, FONT_MODE FontMode)
{
        u8 Font_Size = FontMode & 0xff;
        u8 width;
#if        CHINESE_ENABLE
        u8* pBuff;
#endif

        switch (Font_Size)
        {
                case 12:width = 16; break;
                case 16:width = 16; break;
                case 24:width = 24; break;
                default:width = 16; break;
        }

        while (*pStr != 0)
        {
#if        CHINESE_ENABLE               
                if ((u8)*pStr > 0x80)//汉字
                {
                        if (pHandle->FONT_GetFontLattice != NULL && pHandle->FONT_GetFontLattice(&pBuff, (u8*)pStr, FontMode) == TRUE)        //获取汉字点阵
                        {
                                SSD1306_GRAM_ShowChina(pHandle, x, y, pBuff, FontMode);                                //显示汉字
                        }
                       
                        pStr += 2;
                        if (x > 128 - 16)  //自动换行
                        {
                                x = 0;
                                y += Font_Size;
                        }
                        else
                        {
                                x += width;
                        }
                }
                else //ASCII
#endif
                {
                        SSD1306_GRAM_ShowChar(pHandle, x, y, *pStr, FontMode);
                        pStr++;
                        if (x > 128 - 8)  //自动换行
                        {
                                x = 0;
                                y += Font_Size;
                        }
                        else
                        {
                                x += width / 2;
                        }
                }

        }
}


/*************************************************************************************************************************
* 函数                        :        void SSD1306_GRAM_ShowLattice(u16 x,u16 y,u16 width, u16 height, const u8 LatticeBuff[], bool isInvert)
* 功能                        :        在指定位置显示一个指定大小的点阵数据
* 参数                        :        x,y:显示开始坐标,width:点阵宽度;height:点阵高度;LatticeBuff:点阵数据缓冲区;isInvert:是否反色显示
* 返回                        :        无
* 依赖                        :        画点函数
* 作者                        :        cp1300@139.com
* 时间                        :        2016-03-25
* 最后修改时间         :         2016-03-25
* 说明                        :         用于显示自定义字符
                                        字符高度任意,宽度不足8的倍数需要补充为8的倍数进行存储,但是不显示
                                        字模取模方式:阴码,逐行式,顺向,高字节在前
*************************************************************************************************************************/
void SSD1306_GRAM_ShowLattice(SSD1306_HANDLE* pHandle, u8 x, u8 y, u16 width, u16 height, const u8 LatticeBuff[], bool isInvert)
{
        u16 i, j, k;
        u8* p;
        void (*DrawPoint)(SSD1306_HANDLE* pHandle,u8 i, u8 j);


        u16 w = width / 8;                //计算整数行
        u16 res = width % 8;        //计算非整数行
        p = (u8*)LatticeBuff;

        if (isInvert)        //反显
        {
                DrawPoint = SSD1306_GRAM_ClearPoint;                        //清除点函数
                SSD1306_GRAM_Fill(pHandle,x, y, x + width - 1, y + height - 1);        //填充满区域       
        }
        else //正常模式
        {
                DrawPoint = SSD1306_GRAM_DrawPoint;                        //画点函数
                SSD1306_GRAM_Clear(pHandle,x, y, x + width - 1, y + height - 1);        //清空显示区域
        }


        for (i = 0; i < height; i++)        //逐行绘制
        {
                for (j = 0; j < w; j++)        //整8的倍数的列先进行绘制
                {
                        for (k = 0; k < 8; k++)
                        {
                                if (*p & (0x80 >> k))
                                        (*DrawPoint)(pHandle,x + j * 8 + k, y + i);
                        }
                        p++;
                }
                for (k = 0; k < res; k++)
                {
                        if (*p & (0x80 >> k))
                                (*DrawPoint)(pHandle,x + w * 8 + k, y + i);
                }
                if (res > 0) p++;

        }
}


/*************************************************************************************************************************
* 函数                :        void MGRAM_FONT_GetFontLattice_Hook(SSD1306_HANDLE* pHandle, bool (*FONT_GetFontLattice)(u8** pBuff, const u8* pChinese, FONT_MODE FontMode))
* 功能                :        中文汉字点阵获取接口设置
* 参数                :        pHandle:句柄;FONT_GetFontLattice:中文汉字点阵获取接口指针
                                bool (*FONT_GetFontLattice)(u8** pBuff, const u8* pChinese, FONT_MODE FontMode)
                                pBuff:返回的全局或静态缓冲区,用于存放点阵数据,pChinese:中文字符串;FontMode:字体模式信息
                                返回值:TRUE:成功;FALSE:失败,可能是缓冲区大小不对,字库读取失败,不支持的字体等
* 返回                :        无
* 依赖                :        无
* 作者                :        cp1300@139.com
* 时间                :        2021-09-06
* 最后修改时间 :        2022-05-23
* 说明                :
*************************************************************************************************************************/
void SSD1306_GRAM_GetFontLattice_Hook(SSD1306_HANDLE* pHandle, bool (*FONT_GetFontLattice)(u8** pBuff, const u8* pChinese, FONT_MODE FontMode))
{
        pHandle->FONT_GetFontLattice = FONT_GetFontLattice;
}



#endif //OLED_SSD1306_ENABLE

/*************************************************************************************************************
* 文件名                :        OLED_SSD1306.h
* 功能                        :        OLED_SSD1306 底层驱动
* 作者                        :        cp1300@139.com
* 创建时间                :        2022-10-19
* 最后修改时间        :        2022-10-19
* 详细:                        OLED刷新方式,列行刷新,一次刷新一列的8bit,然后刷新下一列
*************************************************************************************************************/       
#ifndef __OLED_SSD1306_H_
#define __OLED_SSD1306_H_
#include "system.h"
#include "board.h"

#if(OLED_SSD1306_ENABLE) //使能

#define LCD_ASCII_12X24_EN        0        //是否使能12x24大小字符显示

//字体格式
typedef enum
{
        FONT16_DEFAULT = (0x8000 + 16),        //16号,叠加显示
        FONT12_DEFAULT = (0x8000 + 12),        //12号,叠加显示
        FONT16_COVER = (16),                        //16号,覆盖显示
        FONT12_COVER = (12),                        //12号,覆盖显示
        FONT16_REVERSE = (0x4000 + 16),        //16号,反显显示
        FONT12_REVERSE = (0x4000 + 12),        //12号,反显显示
}FONT_MODE;



//SSD1306 句柄定义
typedef struct
{
        u8 LCD_GRAM[8][128];                                                                //显存 [8]8个Page,每8行为1Page;[128]:128列的每个SEG0的行0-行7
        void (*WriteRegIndex)(u8 RegIndex);                                        //写寄存器索引
        void (*WriteByteData)(u8 data);                                                //写8bit数据
        void (*WriteData)(u8 *pData, u16 DataLen);                        //写批量字节数据
        u16 Width;                        //屏幕宽度
        u16 Height;                        //屏幕高度
        //汉字显示接口(pBuff:返回的静态缓冲区,存放点阵数据)
        bool (*FONT_GetFontLattice)(u8** pBuff, const u8* pChinese, FONT_MODE FontMode);        //中文字库点阵获取接口
}SSD1306_HANDLE;


#define SSD1306_ST16X16                        16        //ST16X16


void SSD1306_Init(SSD1306_HANDLE *pHandle);                                //初始化
void SSD1306_ReverseColor(SSD1306_HANDLE *pHandle, bool isReverseColor);        //设置反色显示
void SSD1306_Rotate180(SSD1306_HANDLE *pHandle, bool isRotate);                                //旋转180度显示
void SSD1306_EnableDisplay(SSD1306_HANDLE *pHandle, bool isEnable);                        //是否使能显示
void SSD1306_Clear(SSD1306_HANDLE *pHandle);                        //清屏
void SSD1306_Fill(SSD1306_HANDLE *pHandle);                                //填充


void SSD1306_GRAM_ClearAll(SSD1306_HANDLE* pHandle);                                                                                        //清除全部显存
void SSD1306_GRAM_Up(SSD1306_HANDLE* pHandle, u16 xStart, u16 yStart, u16 xEnd, u16 yEnd);                //更新显存到屏幕
#define SSD1306_GRAM_UpAll(pHandle)        SSD1306_GRAM_Up(pHandle, 0, 0, 127, 63)                                                //更新全部
void SSD1306_GRAM_Fill(SSD1306_HANDLE* pHandle, u16 xStart, u16 yStart, u16 xEnd, u16 yEnd);        //GRAM填充
void SSD1306_GRAM_Clear(SSD1306_HANDLE* pHandle, u16 xStart, u16 yStart, u16 xEnd, u16 yEnd);        //GRAM清除

void SSD1306_GRAM_ShowChina(SSD1306_HANDLE* pHandle, u8 x, u8 y, const u8* p, FONT_MODE FontMode);        //在指显存定位置显示一个指定大小的汉字
void SSD1306_GRAM_ShowChar(SSD1306_HANDLE* pHandle, u8 x, u8 y, u8 Char, FONT_MODE FontMode);                //在显存指定位置显示一个指定大小的Char字符
void SSD1306_GRAM_DrawLine(SSD1306_HANDLE* pHandle, u16 x1, u16 y1, u16 x2, u16 y2);                                //在显存指定位置画线
void SSD1306_GRAM_DrawRectangle(SSD1306_HANDLE* pHandle, u16 x1, u16 y1, u16 x2, u16 y2);                        //在显存指定位置画一个矩形
void SSD1306_GRAM_ShowString(SSD1306_HANDLE* pHandle, u16 x, u16 y, const char* pStr, FONT_MODE FontMode);                                                        //在显存指定位置显示字符串
void SSD1306_GRAM_ShowLattice(SSD1306_HANDLE* pHandle, u8 x, u8 y, u16 width, u16 height, const u8 LatticeBuff[], bool isInvert);        //在指定位置显示一个指定大小的点阵数据
void SSD1306_GRAM_GetFontLattice_Hook(SSD1306_HANDLE* pHandle, bool (*FONT_GetFontLattice)(u8** pBuff, const u8* pChinese, FONT_MODE FontMode));//中文汉字点阵获取接口设置

#endif //OLED_SSD1306_ENABLE

#endif //__OLED_SSD1306_H_



//初始化与测试代码,初始化底层接口,调用GRAM显存更新文字,然后刷新up到屏幕。


//显示UI初始化
void LCD_UI_Init(void)
{
//        u32 ms;
       
        OLED_IO_Init();                                                                     //底层IO初始化
        IIC_Init(OLED_IIC_CH, 400, 100);                                                                //硬件IIC初始化
       
        SYS_DelayMS(2);

        g_OLED_Handle.WriteRegIndex = SSD1306_WriteRegIndex;                //接口初始化:写寄存器索引
        g_OLED_Handle.WriteByteData = SSD1306_WriteByteData;                //接口初始化:写8bit数据
        g_OLED_Handle.WriteData = SSD1306_WriteData;                                        //写批量字节数据       
        SSD1306_Init(&g_OLED_Handle);       
        //SSD1306_Rotate180(&g_OLED_Handle, TRUE);                                                //旋转180度显示

       
    SSD1306_GRAM_ShowString(&g_OLED_Handle, 0, 0, "Welcome!1234567", FONT16_DEFAULT);
    SSD1306_GRAM_Up(&g_OLED_Handle, 0, 0, 16*8-1, 16-1);//更新显存到屏幕
//SSD1306_GRAM_UpAll(&g_OLED_Handle);
  //SSD1306_ReverseColor(&g_OLED_Handle, TRUE);        //设置反色显示
}

————————————————
版权声明:本文为CSDN博主「cp1300」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cp1300/article/details/129084945

使用特权

评论回复
沙发
daichaodai| | 2023-10-17 23:25 | 只看该作者
IIC刷新速度还是有慢了

使用特权

评论回复
板凳
daichaodai| | 2023-10-17 23:25 | 只看该作者
IIC刷新速度还是有慢了

使用特权

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

本版积分规则

77

主题

3305

帖子

3

粉丝