使用硬件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
|
|