找回密码
 立即注册
查看: 51|回复: 1

DMA I2C 非阻塞方式驱动 OLED

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:6
  • 最近打卡:2025-10-08 16:16:22
已绑定手机

3

主题

14

回帖

117

积分

注册会员

积分
117
发表于 2025-10-8 18:49:05 | 显示全部楼层 |阅读模式

DMA I2C 非阻塞方式驱动 OLED


芯片:AI8051U。OLED:SSD1306 0.96 寸 OLED。昨日调试一天的程序,仔细看了例程,终于成功驱动 OLED。

DMA触发发送逻辑固定,但手册里没有写明

其他 DMA,包括串口的 DMA 发送都是直接设置发送触发位就好,而 IIC DMA 触发发送有一个逆天前摇 I2C_StartSendDataRecvACK()(可以在 ai8051u_def.h 中看到这个宏的定义,作用是 产生起始信号+发送+接收ACK ),而且,更重要的是,手册里不写!!手册里不写!!手册里竟然不写!!! 老天,这是要我算命算出来吗?不看例程根本不知道。其他命令无法触发。

触发 DMA 发送需要设置传输数量、开启和关闭 DMA 等等,这些语句的顺序建议严格按照例程来,一句也不要少,哪怕你认为你自己是对的。尤其是 DMA 的 TxAmount 寄存器和 ST 寄存器要写成相同的数量,我之前一直以为多此一举,结果不写还真失败了。而且全过程要保持 I2C 主机中断开着。

非阻塞式驱动 OLED 例程

楼主用以下思路驱动 OLED:

  1. 开启 I2C主机中断,开启 DMA 及其中断,接下来 1024 + 2 个数据字节通过 DMA 来发送(加的两个分别是从机地址和控制字节);
  2. 当 DMA 发送完成时应进入中断,在中断里失能 DMA 及其中断,并发送一个停止信号。
  3. 在停止信号的中断里调用整个发送过程完成的回调函数。

如果 OLED_ADDROLED_CMD 和那 1024 个数据字节必须存放在同一个数组里,看上去比较麻烦,因为可能要修改 OLED 显示的算法。这里我想了一个办法解决这个问题:将 OLED_DispGraph[1024] 换成宏定义,用 pu8I2CDMATxBuffer + 2 代替之,这样就不用改动处理 OLED_DispGraph 的算法了。

代码 1:OLED.h 中的代码

#define OLED_DispGraph                  (pu8I2CDMATxBuffer + 2)

#define OLED_ADDR                       ((u8)0x78)

// OLED I2C sending operation.
#define OLED_I2C_Write(cmd, pdat, size)         I2C_MemWrite_Start(&oled_i2c, OLED_ADDR, cmd, NULL, size)
#define OLED_I2C_BlockWrite(cmd, pdat, size)    \
{   \
    while (I2C_MemWrite_Start(&oled_i2c, OLED_ADDR, cmd, pdat, size) == FALSE);     \
    while (I2C_IsSending(&oled_i2c));      \
}

代码 2:user_i2c.c 中的代码


#include "user_i2c.h"

#define I2C_State_IDLE							0
#define I2C_State_DATA							1
#define I2C_State_STOP							2

#define I2C_Mode_MEMWRITE						1
#define I2C_Mode_SEND							2

#define I2CMSAUX_WDTA_MSK						BIT0
#define I2C_EnableAutoSend()					(SET_REG_BIT(I2CMSAUX, I2CMSAUX_WDTA_MSK))


I2C_Send_Cplt_Callback_t i2c_send_cplt_callback = NULL;


void I2C_Handle_Init(I2C_Handle_t *hi2c, u8 *buf, u16 bufSize)
{
    hi2c->Buf = buf;
    hi2c->BufSize = bufSize;
    hi2c->State = I2C_State_IDLE;
}


void I2C_Set_CpltCallback(I2C_Send_Cplt_Callback_t callback)
{
	i2c_send_cplt_callback = callback;
}


BOOL I2C_IsSending(I2C_Handle_t *hi2c)
{
	return (BOOL)(hi2c->State);
}


static void I2C_SendStart(I2C_Handle_t *hi2c)
{
	// I2C_DisableMasterInt();		// The interrupt must be open
	// I2C_DisableAutoSend();		// this is irrelational

	DMA_I2C_ClearTxFlag();
	hi2c->State = I2C_State_DATA;
	I2C_StartSendDataRecvACK();		// Only this cmd can start the transmission
	DMA_I2C_EnableTxInt();
	DMA_I2C_EnableDMA();			// Set data amount is allowed when dma is off or on
	DMA_I2C_EnableTx();				// Set data amount is allowed only when dma tx is off, either tx off or dma off
	DMA_I2C_TriggerTx();
}


/**
 * @brief I2C sending function.
 * 
 * @param hi2c 		the handle
 * @param slvAddr 	address of slave device
 * @param pdat 		if translate data form other places (not the buffer), then put the addr;
 * 			   		if translate data of the buffer of the handle, put NULL.
 * @param len  		length tend to transfer
 * @return 			BOOL if success
 */
BOOL I2C_Send_Start(I2C_Handle_t *hi2c, u8 slvAddr, u8 *pdat, u16 len)
{
    if (hi2c->State != I2C_State_IDLE || len == 0) return FALSE;

    if (pdat != NULL) memcpy(hi2c->Buf + 1, pdat, min(len, hi2c->BufSize));
	hi2c->Buf[0] = slvAddr;

	DMA_I2C_DisableDMA();
	DMA_I2C_SetTxAmount(len);
	DMA_I2C_SetDMAAmount(len);	// must set the same with TxAmount
	I2C_SendStart(hi2c);

	return TRUE;
}


/**
 * @brief I2C memory write function.
 * 
 * @param hi2c 		the handle
 * @param slvAddr 	address of slave device
 * @param memAddr 	memory address
 * @param pdat 		if translate data form other places (not the buffer), then put the addr;
 * 			   		if translate data of the buffer of the handle, put NULL.
 * @param len 		length tend to transfer
 * @return BOOL 	BOOL if success
 */
BOOL I2C_MemWrite_Start(I2C_Handle_t *hi2c, u8 slvAddr, u8 memAddr, u8 *pdat, u16 len)
{
	if (hi2c->State != I2C_State_IDLE || len == 0) return FALSE;

    if (pdat != NULL) memcpy(hi2c->Buf + 2, pdat, min(len, hi2c->BufSize));
	hi2c->Buf[0] = slvAddr;
	hi2c->Buf[1] = memAddr;
	DMA_I2C_DisableDMA();
	DMA_I2C_SetTxAmount(len + 1);
	DMA_I2C_SetDMAAmount(len + 1);	// must set the same with TxAmount
	I2C_SendStart(hi2c);

	return TRUE;
}


void I2C_IT_Handler(I2C_Handle_t *hi2c)
{
	if (hi2c->State == I2C_State_STOP)
	{
		hi2c->State = I2C_State_IDLE;
		if (i2c_send_cplt_callback) i2c_send_cplt_callback(hi2c);
	}
}


void I2C_DMA_IT_Handler(I2C_Handle_t *hi2c)
{
	if (hi2c->State == I2C_State_DATA)
	{
		// The DMA transmission was just over.
		// Disable DMA and enable master interrupt, then generate a stop condition.
		hi2c->State = I2C_State_STOP;
		DMA_I2C_DisableDMA();
		DMA_I2C_DisableTxInt();
		I2C_Stop();
	}
}


整个工程放这里了:upload 附件:01_learn.zip

我爱STC
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:584
  • 最近打卡:2025-10-15 08:26:15
已绑定手机

86

主题

6274

回帖

1万

积分

超级版主

积分
11734
发表于 7 天前 | 显示全部楼层
手册里怎么没写

截图202510091207041500.jpg

截图202510091207219549.jpg

回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-10-16 03:41 , Processed in 0.113981 second(s), 56 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表