ARM-I2C硬实现

硬件I2C-GD32F4系列的实现

===初始化操作===

在初始化函数里执行以下代码

uint32_t i2cx_scl_port_rcu = RCU_GPIOB;
uint32_t i2cx_scl_port = GPIOB;
uint32_t i2cx_scl_pin = GPIO_PIN_6;
uint32_t i2cx_scl_af = GPIO_AF_4;uint32_t i2cx_sda_port_rcu = RCU_GPIOB;
uint32_t i2cx_sda_port = GPIOB;
uint32_t i2cx_sda_pin = GPIO_PIN_7;
uint32_t i2cx_sda_af = GPIO_AF_4;uint32_t i2cx = I2C0;
uint32_t i2cx_rcu = RCU_I2C0;
uint32_t i2cx_speed = 400000;
/****************** GPIO config **********************/
// 时钟配置
rcu_periph_clock_enable(i2cx_scl_port_rcu);
// 设置复用功能
gpio_af_set(i2cx_scl_port, i2cx_scl_af, i2cx_scl_pin);
// 设置输出模式
gpio_mode_set(i2cx_scl_port, GPIO_MODE_AF, GPIO_PUPD_NONE, i2cx_scl_pin);
gpio_output_options_set(i2cx_scl_port, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, i2cx_scl_pin);// 时钟配置
rcu_periph_clock_enable(i2cx_sda_port_rcu);
// 设置复用功能
gpio_af_set(i2cx_sda_port, i2cx_sda_af, i2cx_sda_pin);
// 设置输出模式
gpio_mode_set(i2cx_sda_port, GPIO_MODE_AF, GPIO_PUPD_NONE, i2cx_sda_pin);
gpio_output_options_set(i2cx_sda_port, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, i2cx_sda_pin);/****************** I2C config  **********************/
i2c_deinit(i2cx);
// 时钟配置
rcu_periph_clock_enable(i2cx_rcu);
// I2C速率配置
i2c_clock_config(i2cx, i2cx_speed, I2C_DTCY_2);// 使能i2c
//i2c_mode_addr_config(i2cx, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0x00);
i2c_enable(i2cx);// i2c ack enable
i2c_ack_config(i2cx, I2C_ACK_ENABLE);
  • 哪个I2C
  • SCL是哪个引脚
  • SDA是哪个引脚
  • 速度是多快

准备两个wait函数

等待指定外设的flag状态变化的函数

#define	TIMEOUT	50000
static uint8_t I2C_wait(uint32_t flag) {uint16_t cnt = 0;while(!i2c_flag_get(i2cx, flag)) {cnt++;if(cnt > TIMEOUT) return 1;}return 0;
}static uint8_t I2C_waitn(uint32_t flag) {uint16_t cnt = 0;while(i2c_flag_get(i2cx, flag)) {cnt++;if(cnt > TIMEOUT) return 1;}return 0;
}

===写操作流程===

开始

/************* start ***********************/
// 等待I2C闲置
if(I2C_waitn(I2C_FLAG_I2CBSY)) return 1;
// start
i2c_start_on_bus(i2cx);
// 等待I2C主设备成功发送起始信号
if(I2C_wait(I2C_FLAG_SBSEND)) return 2;

发送设备地址

注意⚠️,这里是设备的写地址write_addr

/************* device address **************/
// 发送设备地址
i2c_master_addressing(i2cx, write_addr, I2C_TRANSMITTER);
// 等待地址发送完成
if(I2C_wait(I2C_FLAG_ADDSEND)) return 3;
i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);

发送寄存器地址

/************ register address ************/
// 寄存器地址
// 等待发送数据缓冲区为空
if(I2C_wait(I2C_FLAG_TBE)) return 4;// 发送数据
i2c_data_transmit(i2cx, reg);// 等待数据发送完成
if(I2C_wait(I2C_FLAG_BTC)) return 5;

数据发送

/***************** data ******************/
// 发送数据
uint32_t i;
for(i = 0; i < len; i++) {uint32_t d = data[i];// 等待发送数据缓冲区为空if(I2C_wait(I2C_FLAG_TBE)) return 6;// 发送数据i2c_data_transmit(i2cx, d);// 等待数据发送完成if(I2C_wait(I2C_FLAG_BTC)) return 7;
}

停止

/***************** stop ********************/
// stop
i2c_stop_on_bus(i2cx);if(I2C_waitn(I2C_CTL0(i2cx)&I2C_CTL0_STOP)) return 8;

===读操作流程===

开始

/************* start ***********************/
// 等待I2C空闲
if(I2C_waitn(I2C_FLAG_I2CBSY)) return 1;
// 发送启动信号
i2c_start_on_bus(i2cx);
// 等待I2C主设备成功发送起始信号
if(I2C_wait(I2C_FLAG_SBSEND)) return 2;

发送设备地址(写)

/************* device address **************/
// 发送从设备地址
i2c_master_addressing(i2cx, address, I2C_TRANSMITTER);if(I2C_wait(I2C_FLAG_ADDSEND)) return 3;
i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);

发送寄存器地址

/********** register address **************/
// 等待发送缓冲区	
if(I2C_wait(I2C_FLAG_TBE)) return 4;// 发送寄存器地址
i2c_data_transmit(i2cx, reg);// 等待发送数据完成	
if(I2C_wait(I2C_FLAG_BTC)) return 5;

开始

/************* start ***********************/
// 发送再启动信号
i2c_start_on_bus(i2cx);if(I2C_wait(I2C_FLAG_SBSEND)) return 7;

发送设备地址(读)

/************* device address **************/
// 发送从设备地址
i2c_master_addressing(i2cx, address, I2C_RECEIVER);
if(I2C_wait(I2C_FLAG_ADDSEND)) return 8;
i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);

数据读取

/************* data **************/
//ack
i2c_ack_config(i2cx, I2C_ACK_ENABLE);
// 接收一个数据后,自动发送ACK
i2c_ackpos_config(i2cx, I2C_ACKPOS_CURRENT);
// 确认ACK已启用
if(I2C_wait(I2C_CTL0(i2cx) & I2C_CTL0_ACKEN)) return 11;// 读取数据
uint32_t i;
for (i = 0; i < len; i++) {if (i == len - 1) {// 在读取最后一个字节之前,禁用ACK,配置为自动NACKi2c_ack_config(i2cx, I2C_ACK_DISABLE);}// 等待接收缓冲区不为空if(I2C_wait(I2C_FLAG_RBNE)) return 10;data[i] = i2c_data_receive(i2cx);
}

停止

/***************** stop ********************/
i2c_stop_on_bus(i2cx);if(I2C_waitn(I2C_CTL0(i2cx)&I2C_CTL0_STOP)) return 12;

GD32F4寄存器

流程

功能

标记

描述

START

I2C_FLAG_I2CBSY

busy标记。I2C是否占用,没有占用才可以使用。

I2C_FLAG_SBSEND

起始信号发送状态标记。START成功或失败。

数据

设备地址

I2C_FLAG_ADDSEND

地址发送状态标记。成功或失败。

发送

I2C_FLAG_TBE

发送数据寄存器是否为空的标记。为空才可以继续发送。

I2C_FLAG_BTC

发送数据寄存器中数据是否发送完成。

接收

I2C_FLAG_RBNE

接收缓冲区寄存器是否为空的标记。为空才可以继续接收。

STOP

I2C_CTL0_STOP

停止标记位。

完整代码

//I2C0.h
#ifndef __I2C0_H__
#define __I2C0_H__#include "systick.h"
#include "gd32f4xx.h"void I2C0_init();uint8_t I2C0_read(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len);uint8_t I2C0_write(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len);uint8_t I2C0_write2(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t offset, uint32_t len);void I2C0_deinit();#endif

 

//I2C0.c
#include "I2C0.h"#define i2cx	I2C0void I2C0_init() {uint32_t i2cx_scl_port_rcu = RCU_GPIOB;uint32_t i2cx_scl_port = GPIOB;uint32_t i2cx_scl_pin = GPIO_PIN_6;uint32_t i2cx_scl_af = GPIO_AF_4;uint32_t i2cx_sda_port_rcu = RCU_GPIOB;uint32_t i2cx_sda_port = GPIOB;uint32_t i2cx_sda_pin = GPIO_PIN_7;uint32_t i2cx_sda_af = GPIO_AF_4;uint32_t i2cx = I2C0;uint32_t i2cx_rcu = RCU_I2C0;uint32_t i2cx_speed = 400000;/****************** GPIO config **********************/// 时钟配置rcu_periph_clock_enable(i2cx_scl_port_rcu);// 设置复用功能gpio_af_set(i2cx_scl_port, i2cx_scl_af, i2cx_scl_pin);// 设置输出模式gpio_mode_set(i2cx_scl_port, GPIO_MODE_AF, GPIO_PUPD_NONE, i2cx_scl_pin);gpio_output_options_set(i2cx_scl_port, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, i2cx_scl_pin);// 时钟配置rcu_periph_clock_enable(i2cx_sda_port_rcu);// 设置复用功能gpio_af_set(i2cx_sda_port, i2cx_sda_af, i2cx_sda_pin);// 设置输出模式gpio_mode_set(i2cx_sda_port, GPIO_MODE_AF, GPIO_PUPD_NONE, i2cx_sda_pin);gpio_output_options_set(i2cx_sda_port, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, i2cx_sda_pin);/****************** I2C config  **********************/i2c_deinit(i2cx);// 时钟配置rcu_periph_clock_enable(i2cx_rcu);// I2C速率配置i2c_clock_config(i2cx, i2cx_speed, I2C_DTCY_2);// 使能i2ci2c_mode_addr_config(i2cx, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0x00);i2c_enable(i2cx);// i2c ack enablei2c_ack_config(i2cx, I2C_ACK_ENABLE);//i2c_ackpos_config(i2cx, I2C_ACKPOS_CURRENT);}static uint8_t I2C_wait(uint32_t flag) {uint16_t TIMEOUT = 50000;uint16_t cnt = 0;while(!i2c_flag_get(i2cx, flag)) {cnt++;if(cnt > TIMEOUT) return 1;}return 0;
}static uint8_t I2C_waitn(uint32_t flag) {uint16_t TIMEOUT = 50000;uint16_t cnt = 0;while(i2c_flag_get(i2cx, flag)) {cnt++;if(cnt > TIMEOUT) return 1;}return 0;
}uint8_t I2C0_write(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t data_len) {uint8_t address = addr << 1;/************* start ***********************/// 等待I2C闲置if(I2C_waitn(I2C_FLAG_I2CBSY)) return 1;// starti2c_start_on_bus(i2cx);// 等待I2C主设备成功发送起始信号if(I2C_wait(I2C_FLAG_SBSEND)) return 2;/************* device address **************/// 发送设备地址i2c_master_addressing(i2cx, address, I2C_TRANSMITTER);// 等待地址发送完成if(I2C_wait(I2C_FLAG_ADDSEND)) return 3;i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);/************ register address ************/// 寄存器地址// 等待发送数据缓冲区为空if(I2C_wait(I2C_FLAG_TBE)) return 4;// 发送数据i2c_data_transmit(i2cx, reg);// 等待数据发送完成if(I2C_wait(I2C_FLAG_BTC)) return 5;/***************** data ******************/// 发送数据uint32_t i;for(i = 0; i < data_len; i++) {uint32_t d = data[i];// 等待发送数据缓冲区为空if(I2C_wait(I2C_FLAG_TBE)) return 6;// 发送数据i2c_data_transmit(i2cx, d);// 等待数据发送完成if(I2C_wait(I2C_FLAG_BTC)) return 7;}/***************** stop ********************/// stopi2c_stop_on_bus(i2cx);if(I2C_waitn(I2C_CTL0(i2cx)&I2C_CTL0_STOP)) return 8;return 0;
}uint8_t I2C0_write2(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t offset, uint32_t len) {uint8_t address = addr << 1;/************* start ***********************/// 等待I2C闲置if(I2C_waitn(I2C_FLAG_I2CBSY)) return 1;// starti2c_start_on_bus(i2cx);// 等待I2C主设备成功发送起始信号if(I2C_wait(I2C_FLAG_SBSEND)) return 2;/************* device address **************/// 发送设备地址i2c_master_addressing(i2cx, address, I2C_TRANSMITTER);// 等待地址发送完成if(I2C_wait(I2C_FLAG_ADDSEND)) return 3;i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);/************ register address ************/// 寄存器地址// 等待发送数据缓冲区为空if(I2C_wait(I2C_FLAG_TBE)) return 4;// 发送数据i2c_data_transmit(i2cx, reg);// 等待数据发送完成if(I2C_wait(I2C_FLAG_BTC)) return 5;/***************** data ******************/// 发送数据do {// 等待发送数据缓冲区为空if(I2C_wait(I2C_FLAG_TBE)) return 6;// 发送数据i2c_data_transmit(i2cx, *data);data += offset;// 等待数据发送完成if(I2C_wait(I2C_FLAG_BTC)) return 7;} while(--len);/***************** stop ********************/// stopi2c_stop_on_bus(i2cx);if(I2C_waitn(I2C_CTL0(I2C0)&I2C_CTL0_STOP)) return 8;return 0;
}void I2C0_deinit() {}uint8_t I2C0_read(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len) {uint32_t i2cx = I2C0;uint8_t address = addr << 1;/************* start ***********************/// 等待I2C空闲if(I2C_waitn(I2C_FLAG_I2CBSY)) return 1;// 发送启动信号i2c_start_on_bus(i2cx);if(I2C_wait(I2C_FLAG_SBSEND)) return 2;/************* device address **************/// 发送从设备写地址i2c_master_addressing(i2cx, address, I2C_TRANSMITTER);if(I2C_wait(I2C_FLAG_ADDSEND)) return 3;i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);/********** register address **************/// 等待发送缓冲区	if(I2C_wait(I2C_FLAG_TBE)) return 4;// 发送寄存器地址i2c_data_transmit(i2cx, reg);// 等待发送数据完成	if(I2C_wait(I2C_FLAG_BTC)) return 5;	/************* start ***********************/// 发送再启动信号i2c_start_on_bus(i2cx);if(I2C_wait(I2C_FLAG_SBSEND)) return 7;/************* device address **************/// 发送从设备读地址i2c_master_addressing(i2cx, address + 1, I2C_RECEIVER);if(I2C_wait(I2C_FLAG_ADDSEND)) return 8;i2c_flag_clear(i2cx, I2C_FLAG_ADDSEND);/**************** 接收数据data *************///acki2c_ack_config(i2cx, I2C_ACK_ENABLE);// 接收一个数据后,自动发送ACKi2c_ackpos_config(i2cx, I2C_ACKPOS_CURRENT);// 确认ACK已启用if(I2C_wait(I2C_CTL0(i2cx) & I2C_CTL0_ACKEN)) return 11;// 读取数据uint32_t i;for (i = 0; i < len; i++) {if (i == len - 1) {// 在读取最后一个字节之前,禁用ACK,配置为自动NACKi2c_ack_config(i2cx, I2C_ACK_DISABLE);}// 等待接收缓冲区不为空if(I2C_wait(I2C_FLAG_RBNE)) return 10;data[i] = i2c_data_receive(i2cx);}/***************************************//***************** stop ********************/i2c_stop_on_bus(i2cx);if(I2C_waitn(I2C_CTL0(i2cx)&I2C_CTL0_STOP)) return 12;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/92893.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/92893.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WinUI3开发_过渡动画

简介 过渡动画是当发生事件时控件UI状态发生改变时以一种动画形式来演变到另外一种状态&#xff0c;而非瞬间改变&#xff0c;使用一种更加平滑的方式来进行切换&#xff0c;例如下图是文字切换的交叉栅栏效果&#xff1a;还有页面切换动画&#xff1a;在或者是图标动画&#x…

Linux下提权root权限

现在AI工具这么丰富&#xff0c;稍微搜一下就有一个差不多的总结输出。但是&#xff0c;可能还不够详细&#xff0c;或者给得太多~~~今天时间关系&#xff0c;今天只总结了在Linux如何提权到root&#xff0c;并没有写如何进行防护。后面有时间&#xff0c;我再总结一下。命令实…

焊接机器人节能先锋

汽车制造业中&#xff0c;机器人技术的应用已成为推动工业自动化和生产效率提升的重要力量。机器人在焊接、组装、涂装等关键制造环节中扮演着不可或缺的角色。随着工业生产规模的不断扩大&#xff0c;能源消耗和成本控制成为了企业必须面对的重大挑战。尤其是工业焊接用气的大…

MinIO:云原生对象存储的终极指南

MinIO 是什么? MinIO 是一款高性能、云原生的对象存储服务,具有以下优势: 轻量级部署:采用 Go 语言编写,资源占用低,支持快速部署 兼容性强:完全兼容 Amazon S3 API,轻松对接现有应用 高可用架构:支持分布式部署,确保数据持久性和服务可用性 高性能表现:专为云环境…

Spring AOP `MethodInvocation` 工作原理

⚙️ 一、通知到 MethodInterceptor 的转换机制 Spring AOP 通过适配器模式将开发者定义的注解型通知&#xff08;如 Before&#xff09;统一转换为 MethodInterceptor 接口实现&#xff0c;确保所有通知类型能接入同一调用链。以下是转换细节&#xff1a; 1. 适配器实现原理 核…

PPO原论文阅读

一、Introduction1.目前存在的问题&#xff1a;(deep)Q-learning:在一些简单问题上表现不佳&#xff0c;可理解性差基础的policy gradient算法&#xff1a;&#xff08;如REINFORCE&#xff09;鲁棒性差&#xff0c;需要大量数据TRPO&#xff1a;复杂&#xff0c;在包含噪音&am…

零基础也能创作专属歌曲:文心一言+蘑兔AI协同教程

在AI技术飞速发展的今天&#xff0c;音乐创作已不再是专业音乐人的专属领域。通过文心一言与蘑兔AI的协同使用&#xff0c;即使没有音乐基础&#xff0c;也能轻松完成从歌词创作到作曲编曲的全流程。本文将详细拆解操作步骤&#xff0c;助你快速上手&#xff0c;实现音乐创作梦…

图论:搜索问题

提到图论中的搜索问题&#xff0c;首先想到的也就是DFS和BFS了&#xff0c;而提到这两种搜索&#xff0c;那么最典型的题目就是岛屿问题了&#xff0c;下面就练习几道相关的题目&#xff0c;为之后的更深奥的图论学习打下基础&#xff01; 孤岛的总面积 题目链接&#xff1a;…

AI驱动攻防升级,API安全走到关键档口

在数字化转型与AI技术快速发展的双重驱动下&#xff0c;API已成为企业业务与外部世界连接的神经中枢。然而&#xff0c;随着API的深度应用&#xff0c;针对API的攻击规模与复杂性也在持续升级。 API为何频频成为黑客重点盯防的突破口&#xff1f;企业常见的API防护手段是否还能…

网络基础DAY18-动态路由协议基础

动态路由协议基础知识回顾&#xff1a;1.什么是路由&#xff1f; 答&#xff1a;是三层设备转发IP报文的路径信息。 2.路由有哪些来源&#xff1f; 答&#xff1a;1.直连路由2.静态路由3.动态路由 3.有直连路由的条件&#xff1f; 答&#xff1a;1.二层和三层物理接口状态为UP …

axios统一封装规范管理

新建/api/ 1.新建统一处理文件/api/axios.ts import axios from "axios"const http axios.create({baseURL: import.meta.env.VITE_API_BASE_URL, // 从环境变量读取timeout: 10000, });// 请求拦截器&#xff08;如添加 Token&#xff09; http.interceptors.reque…

Java学习第七十四部分——Elasticsearch(ES)

目录 一、前言提要 二、核心特性 三、应用场景 四、主要优势 五、集成方式 六、基础操作 七、高级特性 八、概念类比——与关系型数据库 九、简单示例——实现存储与搜索 十、生态集成——基于Spring Data Elasticsearch 十一、性能优化建议 十二、总结归纳概述 一…

TDengine 转化函数 TO_UNIXTIMESTAMP 用户手册

TDengine TO_UNIXTIMESTAMP 函数用户使用手册 函数概述 TO_UNIXTIMESTAMP 是 TDengine 中的标量函数&#xff0c;用于将符合 ISO8601/RFC3339 标准的日期时间字符串转换为 Unix 时间戳。与 TO_TIMESTAMP 不同&#xff0c;该函数专门处理标准格式的时间字符串&#xff0c;无需指…

Java 中的排序算法详解

目录 一、冒泡排序&#xff08;Bubble Sort&#xff09; 原理​ 二、选择排序&#xff08;Selection Sort&#xff09; 原理​ 三、插入排序&#xff08;Insertion Sort&#xff09; 原理​ 四、快速排序&#xff08;Quick Sort&#xff09; 原理​ 五、归并排序&…

Gitee如何成为国内企业DevOps转型的首选平台?

Gitee如何成为国内企业DevOps转型的首选平台&#xff1f; 在数字化转型浪潮中&#xff0c;DevOps已成为提升企业研发效能的关键引擎。作为国内领先的代码托管与协作平台&#xff0c;Gitee凭借本土化优势与全流程支持能力&#xff0c;正成为越来越多企业DevOps实践的核心载体。本…

​Excel——SUMPRODUCT 函数

SUMPRODUCT 是 Excel 中最强大的函数之一&#xff0c;可以用于 ​多条件求和、加权计算、数组运算​ 等复杂场景。下面通过 ​基础语法 实用案例​ 彻底讲透它的用法&#xff01;​一、基础语法​SUMPRODUCT(数组1, [数组2], [数组3], ...)​功能​&#xff1a;将多个数组的对…

告别虚函数性能焦虑:深入剖析C++多态的现代设计模式

🚀 引言:当多态遇上性能瓶颈 我经常被问到这样一个问题:“既然virtual函数这么方便,为什么在一些高性能场景下,大家却避之不及?” 答案很简单:性能。 在我参与的多个HPC项目和游戏引擎开发中,virtual函数调用往往成为性能分析工具中最显眼的那个红点。一个看似无害…

k8s-MongoDB 副本集部署

前提准备一套 k8s 集群worker 节点上的 /nfs/data 目录挂载到磁盘一、NFS 高可用方案&#xff08;NFSkeepalivedSersync&#xff09;本方案 NFS 的高可用方案&#xff0c;应用服务器为 Client &#xff0c;两台文件服务器分别 Master 和 Slave&#xff0c;使用 keepalived 生成…

BI 系统数据看板全解析:让数据可视化驱动业务决策

BI 系统数据看板全解析&#xff1a;让数据可视化驱动业务决策在 BI 系统中&#xff0c;数据看板是连接原始数据与业务洞察的 “桥梁”。它将零散的业务指标转化为直观的可视化图表&#xff0c;让产品经理、运营人员等角色能快速把握业务动态。一个设计精良的数据看板&#xff0…

图机器学习(14)——社交网络分析

图机器学习&#xff08;14&#xff09;——社交网络分析0. 前言1. 数据集分析1.1 数据集介绍1.2 使用 networkx 加载数据集2. 网络拓扑和社区检测2.1 网络拓扑2.2 社区检测0. 前言 社交网站的崛起是近年来数字媒体领域最活跃的发展趋势之一&#xff0c;数字社交互动已经融入人…