在线时间410 小时
UID3135871
注册时间2016-10-9
NXP金币1981

TA的每日心情 | 无聊 昨天 08:38 |
|---|
签到天数: 705 天 连续签到: 1 天 [LV.9]以坛为家II
版主
  
- 积分
- 4969

- 最后登录
- 2026-11-10
|
本帖最后由 胤幻1988 于 2026-9-18 14:30 编辑
MCXA366VLL自制板IIC完全用TRAE AI写。
我也是第一次搞这玩意,之前都是用豆包查查啥原型函数,分析些小故障。
但是抖音天天刷到AI画板子,AI写程序,搞的我都有点焦虑了。 看舆论,大家都说国外的啥CODEX,Cursor,Copilot比较好,但是咱没这个条件!
只能用国产的TRAE。
我把板子的原理图和MCXA366规格书发给TRAE,然后要求他根据原理图,给板上的24C04写程序。
AI的逻辑还是很清晰的,它先读PDF确定了IIC的引脚,然后再去查这几个引脚的复用情况,然后读当前程序
里面外设的使用情况,然后就是一顿代码输出。一开始对应IIC等待延时,它定义的是局部变量,结果不正常。
还是会卡在SDK里面的等待函数里面,然后它自己做了调整,写了全局的延时,它在MDK宏定义里面,添加了
CONFIG_I2C_RETRY_TIMES=2000000,这个也是NXP SDK留给用户的定义接口。
然后测试,正常了。
AI自己思考:
AI写的C文件:
- /*
- * Copyright 2026 NXP
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- /* 必须在 fsl_lpi2c.h 被包含之前定义:把驱动内部的无限等待改成有限
- * 超时(默认 I2C_RETRY_TIMES=0 会死循环)。总线异常时返回 timeout,
- * 不会把整个系统卡死。值为等待循环次数,远大于正常传输所需。 */
- #ifndef CONFIG_I2C_RETRY_TIMES
- #define CONFIG_I2C_RETRY_TIMES 2000000U
- #endif
- #include "fsl_device_registers.h"
- #include "fsl_lpi2c.h"
- #include "fsl_port.h"
- #include "fsl_clock.h"
- #include "fsl_reset.h"
- #include "at24c04.h"
- #include <string.h>
- /*******************************************************************************
- * 本地配置
- ******************************************************************************/
- /*! LPI2C 外设实例 */
- #define EEPROM_LPI2C LPI2C2
- /*! I2C 引脚(ALT3 = LPI2C2) */
- #define EEPROM_I2C_PORT PORT1
- #define EEPROM_SDA_PIN 8U
- #define EEPROM_SCL_PIN 9U
- /*! LPI2C2 源时钟:FRO_LF_DIV = 12MHz,分频 1 */
- #define EEPROM_CLK_FREQ 12000000U
- /*! I2C 总线速率 */
- #define EEPROM_BAUDRATE 100000U
- /*! 页写入大小 */
- #define EEPROM_PAGE_SIZE 16U
- /*! 单块字节数(A8 选择的 256 字节区域) */
- #define EEPROM_BLOCK_BYTES 256U
- /*! EEPROM 总容量 */
- #define EEPROM_TOTAL_BYTES 512U
- /*! 块0/块1 的 7 位从地址。
- * AT24C04 器件地址字节 = 1010 A8 A1 A0 R/W,A1=A0=0(接地):
- * A8=0 -> 8位 0xA0 -> 7位 0x50;A8=1 -> 8位 0xA2 -> 7位 0x51。
- * 注意 LPI2C API 用 7 位地址(内部左移),块1是 0x51 不是 0x52。 */
- #define EEPROM_SLAVE_BLOCK0 0x50U
- #define EEPROM_SLAVE_BLOCK1 0x51U
- /*! ACK 轮询最大次数(每次间隔粗略 0.2ms,共约 12ms,大于 tWR=5ms) */
- #define EEPROM_ACK_POLL_MAX 60U
- /*******************************************************************************
- * 内部函数
- ******************************************************************************/
- /*!
- * @brief 根据 EEPROM 地址取 7 位从地址
- * bit8(A8)=0 -> 0x50;A8=1 -> 0x51
- */
- static uint16_t EEPROM_GetSlaveAddress(uint16_t memAddr)
- {
- uint16_t addr;
- if ((memAddr & (uint16_t)0x0100U) != 0U)
- {
- addr = EEPROM_SLAVE_BLOCK1;
- }
- else
- {
- addr = EEPROM_SLAVE_BLOCK0;
- }
- return addr;
- }
- /*!
- * @brief 粗略忙等待
- * @note 故意不用 board.c 的 my_10usDelay():它会把 my_10usDelayCnt
- * 清零,而 LED 非阻塞闪烁正以该计数器为时间基准,清零会打乱
- * LED 的 500ms 节拍。这里只做粗略间隔(供 ACK 轮询),不影响
- * 任何全局计数器。
- */
- static void EEPROM_ShortBusyWait(uint32_t loops)
- {
- volatile uint32_t i;
- for (i = 0U; i < loops; i++)
- {
- __NOP();
- }
- }
- /*!
- * @brief ACK 轮询等待器件内部写周期完成
- * 烧写期间器件不响应地址(NACK),完成后恢复 ACK。
- * 只发 START + 器件地址(W) + STOP(subaddressSize/dataSize 均为 0)。
- * @retval kStatus_Success 器件已应答;kStatus_Timeout 超时
- */
- static status_t EEPROM_AckPoll(uint16_t slaveAddress)
- {
- lpi2c_master_transfer_t xfer;
- uint32_t tries;
- status_t result = kStatus_Timeout;
- (void)memset(&xfer, 0, sizeof(xfer));
- xfer.slaveAddress = slaveAddress;
- xfer.direction = kLPI2C_Write;
- xfer.flags = kLPI2C_TransferDefaultFlag;
- for (tries = 0U; tries < EEPROM_ACK_POLL_MAX; tries++)
- {
- if (LPI2C_MasterTransferBlocking(EEPROM_LPI2C, &xfer) == kStatus_Success)
- {
- result = kStatus_Success;
- break;
- }
- /* 粗略约 0.2ms 后重试(@240MHz,约 10000 次循环) */
- EEPROM_ShortBusyWait(10000U);
- }
- return result;
- }
- /*******************************************************************************
- * Public API
- ******************************************************************************/
- void AT24C04_Init(void)
- {
- lpi2c_master_config_t masterConfig;
- /* 1) PORT1 时钟与复位 */
- CLOCK_EnableClock(kCLOCK_GatePORT1);
- RESET_ReleasePeripheralReset(kPORT1_RST_SHIFT_RSTn);
- /* 2) I2C 引脚复用:P1_8 SDA / P1_9 SCL,均为 ALT3。
- * I2C 开漏输出由 LPI2C 外设驱动,外部已有 4.7K 上拉。 */
- PORT_SetPinMux(EEPROM_I2C_PORT, EEPROM_SDA_PIN, kPORT_MuxAlt3);
- PORT_SetPinMux(EEPROM_I2C_PORT, EEPROM_SCL_PIN, kPORT_MuxAlt3);
- /* 3) 开启两个引脚的输入缓冲(PCR IBE=1)。
- * 本芯片 PORT 输入缓冲默认关闭;LPI2C 主机要靠 SCL 回读做
- * 时钟同步、靠 SDA 回读判断 ACK/仲裁,不开 IBE 会读到恒低电平,
- * START 后永远等不到 SCL 拉高而死等(写法与 uart4.c 一致)。 */
- EEPROM_I2C_PORT->PCR[EEPROM_SDA_PIN] =
- ((EEPROM_I2C_PORT->PCR[EEPROM_SDA_PIN] & (~PORT_PCR_IBE_MASK)) | PORT_PCR_IBE(1U));
- EEPROM_I2C_PORT->PCR[EEPROM_SCL_PIN] =
- ((EEPROM_I2C_PORT->PCR[EEPROM_SCL_PIN] & (~PORT_PCR_IBE_MASK)) | PORT_PCR_IBE(1U));
- /* 4) LPI2C2 时钟:挂 12MHz FRO_LF_DIV,分频 1,开时钟门控,解复位 */
- CLOCK_AttachClk(kFRO_LF_DIV_to_LPI2C2);
- CLOCK_SetClockDiv(kCLOCK_DivLPI2C2, 1U);
- CLOCK_EnableClock(kCLOCK_GateLPI2C2);
- RESET_ReleasePeripheralReset(kLPI2C2_RST_SHIFT_RSTn);
- /* 5) 主机配置:100kHz 标准模式 */
- LPI2C_MasterGetDefaultConfig(&masterConfig);
- masterConfig.baudRate_Hz = EEPROM_BAUDRATE;
- LPI2C_MasterInit(EEPROM_LPI2C, &masterConfig, EEPROM_CLK_FREQ);
- }
- status_t AT24C04_WriteByte(uint16_t addr, uint8_t data)
- {
- return AT24C04_Write(addr, &data, 1U);
- }
- status_t AT24C04_ReadByte(uint16_t addr, uint8_t *data)
- {
- if (data == NULL)
- {
- return kStatus_InvalidArgument;
- }
- return AT24C04_Read(addr, data, 1U);
- }
- status_t AT24C04_Write(uint16_t addr, const uint8_t *data, uint16_t len)
- {
- status_t result = kStatus_Success;
- if ((data == NULL) || (len == 0U))
- {
- return kStatus_InvalidArgument;
- }
- /* 地址 + 长度不能超出 512 字节容量(先转 32 位防止加法溢出) */
- if (((uint32_t)addr + (uint32_t)len) > EEPROM_TOTAL_BYTES)
- {
- return kStatus_InvalidArgument;
- }
- while (len > 0U)
- {
- lpi2c_master_transfer_t xfer;
- uint16_t pageOffset;
- uint16_t pageRemain;
- uint16_t chunk;
- /* 计算当前 16 字节页内还能写多少,自动跨页;
- * addr % 16 在跨块(0x0FF->0x100)时同样成立 */
- pageOffset = (uint16_t)(addr % EEPROM_PAGE_SIZE);
- pageRemain = (uint16_t)(EEPROM_PAGE_SIZE - pageOffset);
- chunk = (len < pageRemain) ? len : pageRemain;
- (void)memset(&xfer, 0, sizeof(xfer));
- xfer.slaveAddress = EEPROM_GetSlaveAddress(addr);
- xfer.direction = kLPI2C_Write;
- xfer.subaddress = (uint32_t)(addr & 0x00FFU); /* 1 字节字地址 */
- xfer.subaddressSize = 1U;
- xfer.data = (void *)data;
- xfer.dataSize = chunk;
- xfer.flags = kLPI2C_TransferDefaultFlag;
- result = LPI2C_MasterTransferBlocking(EEPROM_LPI2C, &xfer);
- if (result != kStatus_Success)
- {
- break;
- }
- /* 等待本页内部烧写完成,再写下一页 */
- result = EEPROM_AckPoll(xfer.slaveAddress);
- if (result != kStatus_Success)
- {
- break;
- }
- addr += chunk;
- data += chunk;
- len -= chunk;
- }
- return result;
- }
- status_t AT24C04_Read(uint16_t addr, uint8_t *buf, uint16_t len)
- {
- status_t result = kStatus_Success;
- if ((buf == NULL) || (len == 0U))
- {
- return kStatus_InvalidArgument;
- }
- if (((uint32_t)addr + (uint32_t)len) > EEPROM_TOTAL_BYTES)
- {
- return kStatus_InvalidArgument;
- }
- while (len > 0U)
- {
- lpi2c_master_transfer_t xfer;
- uint16_t blockOffset;
- uint16_t blockRemain;
- uint16_t chunk;
- /* 单块内连续读在 256 字节处回绕,因此跨块(0x0FF->0x100)
- * 必须分两次传输。计算当前块内剩余可读字节。 */
- blockOffset = (uint16_t)(addr % EEPROM_BLOCK_BYTES);
- blockRemain = (uint16_t)(EEPROM_BLOCK_BYTES - blockOffset);
- chunk = (len < blockRemain) ? len : blockRemain;
- (void)memset(&xfer, 0, sizeof(xfer));
- xfer.slaveAddress = EEPROM_GetSlaveAddress(addr);
- xfer.direction = kLPI2C_Read;
- xfer.subaddress = (uint32_t)(addr & 0x00FFU); /* 1 字节字地址 */
- xfer.subaddressSize = 1U;
- xfer.data = buf;
- xfer.dataSize = chunk;
- xfer.flags = kLPI2C_TransferDefaultFlag;
- result = LPI2C_MasterTransferBlocking(EEPROM_LPI2C, &xfer);
- if (result != kStatus_Success)
- {
- break;
- }
- addr += chunk;
- buf += chunk;
- len -= chunk;
- }
- return result;
- }
复制代码 AI写的H文件:
- /*
- * Copyright 2026 NXP
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- #ifndef _AT24C04_H_
- #define _AT24C04_H_
- #include "fsl_common.h"
- /*******************************************************************************
- * 说明
- * AT24C04D I2C EEPROM 驱动(4Kbit = 512 字节)
- *
- * 硬件映射(依据核心板原理图 SCH_MCXA366_100P_CoreBoard_2026-08-31,U5):
- * SDA -> P1_8 / LPI2C2_SDA (ALT3,R29 4.7K 上拉到 +3V3)
- * SCL -> P1_9 / LPI2C2_SCL (ALT3,R28 4.7K 上拉到 +3V3)
- * A0/A1 引脚接地;WP 接地(写保护关闭,可正常写入)
- *
- * 器件地址(7 位,AT24C04 地址 = 1010 A8 A1 A0 R/W):
- * 块0:地址 0x000~0x0FF,A8=0,从地址 0x50
- * 块1:地址 0x100~0x1FF,A8=1,从地址 0x51
- * 字地址为 1 字节,A8 通过从地址位选择。
- *
- * 器件参数:
- * 页写入大小 16 字节;内部写周期 tWR 最大 5ms
- * I2C 速率配置为 100 kHz 标准模式
- *
- * @note 写函数内部会阻塞等待(ACK 轮询,每页最多约 5ms),
- * 仅在主循环 / 普通任务上下文调用,不要在中断中调用。
- ******************************************************************************/
- #if defined(__cplusplus)
- extern "C" {
- #endif /* __cplusplus */
- /*!
- * @brief 初始化 AT24C04:配置 P1_8/P1_9 复用 ALT3、LPI2C2 时钟(12MHz)、
- * 主机模式 100kHz。
- */
- void AT24C04_Init(void);
- /*!
- * @brief 向指定地址写入一个字节(内部等待写周期完成)
- * @param addr EEPROM 地址,范围 0x000~0x1FF
- * @param data 待写入字节
- * @retval kStatus_Success 成功;其他值为失败(超时 / 参数错误 / NACK)
- */
- status_t AT24C04_WriteByte(uint16_t addr, uint8_t data);
- /*!
- * @brief 从指定地址读取一个字节
- * @param addr EEPROM 地址,范围 0x000~0x1FF
- * @param data 输出读取的字节
- * @retval kStatus_Success 成功;其他值为失败
- */
- status_t AT24C04_ReadByte(uint16_t addr, uint8_t *data);
- /*!
- * @brief 向 EEPROM 写入任意长度数据
- * 内部自动按 16 字节页边界切分,并自动处理块0/块1
- * (0x0FF 到 0x100)的从地址切换,每页写完后 ACK 轮询等待。
- * @param addr 起始地址,范围 0x000~0x1FF
- * @param data 待写入数据
- * @param len 字节数;要求 addr + len 不超过 512
- * @retval kStatus_Success 成功;其他值为失败
- */
- status_t AT24C04_Write(uint16_t addr, const uint8_t *data, uint16_t len);
- /*!
- * @brief 从 EEPROM 读取任意长度数据(顺序读)
- * 内部自动处理块0/块1边界(单块内连续读在 256 字节处回绕,
- * 跨块时分两次传输)。
- * @param addr 起始地址,范围 0x000~0x1FF
- * @param buf 接收缓冲
- * @param len 字节数;要求 addr + len 不超过 512
- * @retval kStatus_Success 成功;其他值为失败
- */
- status_t AT24C04_Read(uint16_t addr, uint8_t *buf, uint16_t len);
- #if defined(__cplusplus)
- }
- #endif /* __cplusplus */
- #endif /* _AT24C04_H_ */
复制代码 经过测试:
给我的感觉还是很不错,看了以后这个趋势越来越明显了。
hello_world_AI.rar
(1.74 MB, 下载次数: 0)
|
|