当前位置: 首页 > news >正文

采用GD32F103C8T6开发板的硬件I2C通信实现ADS1115的模拟电压读取(附源码下载)

本文采用GD32F103C8T6开发板的标准库GD32F10x_Firmware_Library_V2.6.0,利用标准库的硬件I2C通信,实现了ADS1115的四个通道模拟电压读取。

本程序实现了GD32的硬件I2C通信,比软件模拟读取的速度快很多;

处理了通信失败的异常;

添加了超时通信,增强了程序的稳定性。

源码下载地址:https://gitee.com/gd_15/gd32-f103c8t6-ads1115.git

程序使用keil打开路径GD32F10x_Firmware_Library_V2.6.0\ Template\ Keil5_project \Project.uvprojx

在官网下载了GD32F10x_Firmware_Library_V2.6.0标准库。

然后直接在标准库里面添加文件夹ads1115_driver,这个文件夹里面包含了开发板PB10和PB11针脚的初始化,本人GD32F103C8T6开发板是在淘宝网的“光子物联企业店”的核心板;
ADS1115模块是在“电子爱好者之家缘起即”购买的。个人买了学习使用。

其主要连线如下:

GD32F103C8T6 ---- ADS1115

PB10 ------ SCL

PB11 ------- SDA

3.3V -------- VDD

GND ------- GND

GND ------- ADDR

PB1 -------- ALRT

其主要代码如下:

//ads1115_driver.c #include <stdio.h> #include "ads1115_driver.h" #include "i2c_driver.h" #define ADS1115_ADDR (ADS1115_ADDRESS_GND) int32_t ads1115_write_register(uint8_t address, uint8_t reg, uint16_t value) { int32_t s = i2c_wait_idle(); if(s != ADS_I2C_SUCCESS){ return s; } s = i2c_start(); if(s != ADS_I2C_SUCCESS){ return s; } s = i2c_send_address(address, I2C_TRANSMITTER); if(s != ADS_I2C_SUCCESS){ goto Fail; } s = i2c_send_byte(reg); if(s != ADS_I2C_SUCCESS){ goto Fail; } s = i2c_send_byte(value >> 8); if(s != ADS_I2C_SUCCESS){ goto Fail; } s = i2c_send_byte(value & 0xFF); if(s != ADS_I2C_SUCCESS){ goto Fail; } s = i2c_stop(); return s; Fail: i2c_stop(); return s; } int32_t ads1115_read_register(uint8_t address, uint8_t reg, uint16_t* result) { uint8_t data[2] = {0}; int32_t s = i2c_wait_idle(); if(s != ADS_I2C_SUCCESS){ return s; } s = i2c_start(); if(s != ADS_I2C_SUCCESS){ return s; } s = i2c_send_address(address, I2C_TRANSMITTER); if(s != ADS_I2C_SUCCESS){ goto Fail; } s = i2c_send_byte(reg); if(s != ADS_I2C_SUCCESS){ goto Fail; } s = i2c_stop(); if(s != ADS_I2C_SUCCESS){ goto Fail; } s = i2c_start(); if(s != ADS_I2C_SUCCESS){ goto Fail; } s = i2c_send_address(address, I2C_RECEIVER); if(s != ADS_I2C_SUCCESS){ goto Fail; } s = i2c_read_byte(data,sizeof(data)/sizeof(data[0])); if(s != ADS_I2C_SUCCESS){ goto Fail; } s = i2c_stop(); if(s != ADS_I2C_SUCCESS){ goto Fail; } *result = (data[0] << 8) | data[1]; return ADS1115_OK; Fail: i2c_stop(); return s; } int32_t ads1115_init(void) { i2c_config(); return ADS1115_OK; } int32_t ads1115_read_channel(uint8_t channel, uint16_t* out,uint32_t* tmout) { uint32_t timeout=0; uint16_t config = 0; uint32_t s = 0; config = ADS1115_CONFIG_OS_SINGLE| ADS1115_CONFIG_GAIN_4_096V| ADS1115_CONFIG_MODE_SINGLE_SHOT | ADS1115_CONFIG_DR_128SPS | ADS1115_CONFIG_CMODE_TRAD| ADS1115_CONFIG_CPOL_ACTIVE_LOW | ADS1115_CONFIG_COMP_NONLAT | ADS1115_CONFIG_COMP_QUE_ASSERT1; switch(channel) { case 0: config |=ADS1115_CONFIG_MUX_SINGLE_0; break; case 1: config |=ADS1115_CONFIG_MUX_SINGLE_1; break; case 2: config |=ADS1115_CONFIG_MUX_SINGLE_2; break; case 3: config |=ADS1115_CONFIG_MUX_SINGLE_3; break; default: return ADS1115_INVALID_PARAM; } s = ads1115_write_register(ADS1115_ADDR, ADS1115_REG_CONFIG, config); if(s != ADS1115_OK){ return s; } // 等待转换完成 while(gpio_input_bit_get(GPIOB, GPIO_PIN_1) == RESET){ if(timeout++ >100000){ return ADS1115_CONVERSION_TIMEOUT; // 超时 } } *tmout = timeout; s = ads1115_read_register(ADS1115_ADDR, ADS1115_REG_CONVERSION, out); return s; } float ads1115_convert_to_voltage(uint16_t adc) { // 假设使用4.096V增益 uint16_t cal = (adc > 0x8000 ?(0xFFFF - adc):adc); return ((float)cal/32768.0)*4.096; }
//ads1115_driver.h #ifndef ADS1115_DRIVER_H #define ADS1115_DRIVER_H #include "gd32f10x.h" #define ADS1115_MODE_RECEIVE_BIT (0) //ADS is receive mode #define ADS1115_MODE_TRANSMIT_BIT (1) //ADS is transmit mode #define ADS1115_ADDRESS_GND (0x48) //default #define ADS1115_ADDRESS_VDD (0x49) #define ADS1115_ADDRESS_SDA (0x4A) #define ADS1115_ADDRESS_SCL (0x4B) /*four registers [7:2] Always write 000000b ** P[1:0] 00b */ #define ADS1115_REG_CONVERSION (0x00) //00b : Conversion register #define ADS1115_REG_CONFIG (0x01) //01b : Config register #define ADS1115_REG_LO_THRESH (0x02) //10b : Lo_thresh register #define ADS1115_REG_HI_THRESH (0x03) //11b : Hi_thresh register /* ** [15] [14:12] [11:9] [8] [7:5] [4] [3] [2] [1:0] ** OS MUX[2:0] PGA[2:0] MODE DR[2:0] COMP_MODE COMP_POL COMP_LAT COMP_QUE[1:0] */ //Operational status or single-shot conversion start #define ADS1115_CONFIG_OS_SINGLE (0x8000) //Input multiplexer configuration #define ADS1115_CONFIG_MUX_SINGLE_0 (0x4000) //AIN0 100b #define ADS1115_CONFIG_MUX_SINGLE_1 (0x5000) //AIN1 101b #define ADS1115_CONFIG_MUX_SINGLE_2 (0x6000) //AIN2 110b #define ADS1115_CONFIG_MUX_SINGLE_3 (0x7000) //AIN3 111b //Programmable gain amplifier configuration #define ADS1115_CONFIG_GAIN_6_144V (0x0000) //FSR=6.144V 000b #define ADS1115_CONFIG_GAIN_4_096V (0x0200) //FSR=4.096V 001b #define ADS1115_CONFIG_GAIN_2_048V (0x0400) //FSR=2.048V 010b default #define ADS1115_CONFIG_GAIN_1_024V (0x0600) //FSR=1.024V 011b #define ADS1115_CONFIG_GAIN_0_512V (0x0800) //FSR=0.512V 100b #define ADS1115_CONFIG_GAIN_0_256V (0x0A00) //FSR=0.256V 101b /* ** Device operating mode ** 0b : Continuous-conversion mode ** 1b : Single-shot mode or power-down state (default) */ #define ADS1115_CONFIG_MODE_CONTINUOUS (0x0000) #define ADS1115_CONFIG_MODE_SINGLE_SHOT (0x0100) //default //Data rate #define ADS1115_CONFIG_DR_008SPS (0x0000) //000b #define ADS1115_CONFIG_DR_016SPS (0x0020) //001b #define ADS1115_CONFIG_DR_032SPS (0x0040) //010b #define ADS1115_CONFIG_DR_064SPS (0x0060) //011b #define ADS1115_CONFIG_DR_128SPS (0x0080) //100b default #define ADS1115_CONFIG_DR_250SPS (0x00A0) //101b #define ADS1115_CONFIG_DR_475SPS (0x00C0) //110b #define ADS1115_CONFIG_DR_860SPS (0x00E0) //111b /* * Comparator mode * 0b : Traditional comparator (default) * 1b : Window comparator */ #define ADS1115_CONFIG_CMODE_TRAD (0x0000) #define ADS1115_CONFIG_CMODE_WINDOW (0x0010) /* * Comparator polarity * 0b : Active low (default) * 1b : Active high */ #define ADS1115_CONFIG_CPOL_ACTIVE_LOW (0x0000) #define ADS1115_CONFIG_CPOL_ACTIVE_HIGH (0x0008) /* * Latching comparator * 0b : Nonlatching comparator(default) * 1b : Latching comparator */ #define ADS1115_CONFIG_COMP_NONLAT (0x0000) #define ADS1115_CONFIG_COMP_LATCHING (0x0004) /* * Comparator queue and disable * 00b : Assert after one conversion * 01b : Assert after two conversions * 10b : Assert after four conversions * 11b : Disable comparator and set ALERT/RDY pin to high-impedance (default) */ #define ADS1115_CONFIG_COMP_QUE_ASSERT1 (0x0000) #define ADS1115_CONFIG_COMP_QUE_ASSERT2 (0x0001) #define ADS1115_CONFIG_COMP_QUE_ASSERT4 (0x0002) #define ADS1115_CONFIG_COMP_QUE_DISABLE (0x0003) int32_t ads1115_write_register(uint8_t address, uint8_t reg, uint16_t value); int32_t ads1115_read_register(uint8_t address, uint8_t reg, uint16_t* result); int32_t ads1115_init(void); int32_t ads1115_read_channel(uint8_t channel, uint16_t* out, uint32_t* tmout); float ads1115_convert_to_voltage(uint16_t adc_value); /* */ typedef enum { ADS1115_OK = 0, ADS1115_ERROR = 0x100, ADS1115_CONVERSION_TIMEOUT, ADS1115_ERROR_TIMEOUT, ADS1115_INVALID_PARAM, ADS1115_COMM_ERROR } ads1115_status; #endif
//i2c_driver.c #include "gd32f10x.h" #include "i2c_driver.h" //Ö÷´ÓģʽÏ£¬Õâ¸öµØÖ·¿ÉÒÔÈÎÒ⣬ûʲôÓà #define I2C1_OWN_ADDRESS7 (0x70) #define I2C_TIMEOUT 0xFFFF void i2c_config(void) { /* enable GPIOB clock */ rcu_periph_clock_enable(RCU_GPIOB); /* enable I2C1 clock */ rcu_periph_clock_enable(RCU_I2C1); //SCL PB10, SDA PB11 gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_10 | GPIO_PIN_11); // ALERT/RDY gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_1); i2c_deinit(I2C1); /* I2C clock configure */ i2c_clock_config(I2C1, 100000, I2C_DTCY_2); /* I2C address configure */ i2c_mode_addr_config(I2C1, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, I2C1_OWN_ADDRESS7); /* enable I2C1 */ i2c_enable(I2C1); /* enable acknowledge */ i2c_ack_config(I2C1, I2C_ACK_ENABLE); } int32_t i2c_start(void) { uint32_t timeout = 0; i2c_start_on_bus(I2C1); while(i2c_flag_get(I2C1, I2C_FLAG_SBSEND) == RESET){ if(timeout++ > I2C_TIMEOUT){ return ADS_I2C_ERROR_START; } } return ADS_I2C_SUCCESS; } int32_t i2c_stop(void) { uint32_t timeout = 0; i2c_stop_on_bus(I2C1); while(I2C_CTL0(I2C1)&0x0200){ if(timeout++ >I2C_TIMEOUT ){ return ADS_I2C_ERROR_STOP; } }; return ADS_I2C_SUCCESS; } int32_t i2c_wait_idle(void) { uint32_t timeout = 0; while((i2c_flag_get(I2C1, I2C_FLAG_I2CBSY)==SET)){ if(timeout++ > I2C_TIMEOUT){ return ADS_I2C_ERROR_IDLE; } } return ADS_I2C_SUCCESS; } int32_t i2c_send_address(uint8_t slave_addr, uint32_t direction) { uint32_t timeout = 0; i2c_master_addressing(I2C1, slave_addr << 1, direction); while(i2c_flag_get(I2C1, I2C_FLAG_ADDSEND) == RESET){ if(timeout++ > I2C_TIMEOUT){ return ADS_I2C_ERROR_ADDSEND; } } i2c_flag_clear(I2C1, I2C_FLAG_ADDSEND); return ADS_I2C_SUCCESS; } int32_t i2c_send_byte(uint8_t data) { uint32_t timeout = 0; i2c_data_transmit(I2C1, data); while(i2c_flag_get(I2C1, I2C_FLAG_BTC) == RESET){ if(timeout++ > I2C_TIMEOUT){ return ADS_I2C_ERROR_BTC; } } return ADS_I2C_SUCCESS; } int32_t i2c_read_byte(uint8_t* out, uint8_t len) { int i = 0; uint32_t timeout = 0; i2c_ack_config(I2C1, I2C_ACK_ENABLE); for(i = 0; i< len; ++i){ timeout = 0; while(i2c_flag_get(I2C1, I2C_FLAG_RBNE) == RESET){ if(timeout++ > I2C_TIMEOUT){ i2c_ack_config(I2C1, I2C_ACK_ENABLE); return ADS_I2C_ERROR_RBNE; } } out[i] = i2c_data_receive(I2C1); //¶ÁÈ¡ÏÂÒ»¸ö×Ö½ÚµÄʱºòÐèÒªDISABLE,·ñÔò»á³ö´í!!! i2c_ack_config(I2C1, I2C_ACK_DISABLE); } i2c_ack_config(I2C1, I2C_ACK_ENABLE); return ADS_I2C_SUCCESS; }
//i2c_driver.h #ifndef I2C_DRIVER_H #define I2C_DRIVER_H #include "gd32f10x.h" #define I2C_TIMEOUT 0xFFFF typedef enum { ADS_I2C_SUCCESS = 0, ADS_I2C_ERROR =0x200, ADS_I2C_ERROR_IDLE, ADS_I2C_ERROR_START, ADS_I2C_ERROR_ADDSEND, ADS_I2C_ERROR_BTC, ADS_I2C_ERROR_RBNE, ADS_I2C_ERROR_STOP, ADS_I2C_ERROR_TIMEOUT } ads1115_i2c_status; void i2c_config(void); int32_t i2c_start(void); int32_t i2c_stop(void); int32_t i2c_wait_idle(void); int32_t i2c_send_address(uint8_t address, uint32_t direction); int32_t i2c_send_byte(uint8_t data); int32_t i2c_read_byte(uint8_t* out, uint8_t len); //void delay_1ms(uint32_t count); #endif
//main.c /*! \file main.c \brief master transmitter \version 2025-08-08, V2.6.0, firmware for GD32F10x */ /* Copyright (c) 2025, GigaDevice Semiconductor Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <math.h> #include "gd32f10x.h" #include "gd32f10x_eval.h" #include "SysTick.h" #include <stdio.h> #include "ads1115_driver.h" /*! \brief toggle the led every 500ms \param[in] none \param[out] none \retval none */ void led_spark(void) { static __IO uint32_t timingdelaylocal = 0U; if(timingdelaylocal){ if(timingdelaylocal < 500U){ gd_eval_led_on(LED2); }else{ gd_eval_led_off(LED2); } timingdelaylocal--; }else{ timingdelaylocal = 1000U; } } //#define I2C0_SLAVE_ADDRESS7 (ADS1115_ADDR_WRITE) /*! \brief main function \param[in] none \param[out] none \retval none */ int main(void) { uint32_t i = 0; uint32_t s = 0; systick_config(); gd_eval_com_init(EVAL_COM0); ads1115_init(); while(1){ float voltage[4]; uint32_t tmCnt = 0; for(i = 0; i < 4; ++i){ uint16_t out = 0; uint32_t cnt = 0; s = ads1115_read_channel(i, &out,&cnt); if(s != ADS1115_OK){ printf("fail channel %d resule:%x\n",i,s); break; }else{ if(cnt > tmCnt){ tmCnt = cnt; } //printf("success channel %d value:%u voltage:%f\n",i, out, ads1115_convert_to_voltage(out)); } voltage[i] = ads1115_convert_to_voltage(out); delay_1ms(100); } if(i != 4){ printf("fail channel %d resule:%x\n",i,s); }else{ int j = 0; for(j = 0;j<4;++j){ printf("%9.6f ",voltage[j]); } printf(" %x \n", tmCnt); } } } #ifdef GD_ECLIPSE_GCC /* retarget the C library printf function to the USART, in Eclipse GCC environment */ int __io_putchar(int ch) { usart_data_transmit(EVAL_COM0, (uint8_t) ch ); while(RESET == usart_flag_get(EVAL_COM0, USART_FLAG_TBE)); return ch; } #else /* retarget the C library printf function to the USART */ int fputc(int ch, FILE *f) { usart_data_transmit(EVAL_COM0, (uint8_t)ch); while(RESET == usart_flag_get(EVAL_COM0, USART_FLAG_TBE)); return ch; } #endif /* GD_ECLIPSE_GCC */

程序运行后的结果如下:


http://www.jsqmd.com/news/358862/

相关文章:

  • CANN生态中的算子测试框架:确保AI计算正确性与性能的基石
  • 抖音自动回复蓝字卡片跳转微信H5开源
  • CANN与开源生态:如何融入并赋能主流AI框架的NPU后端支持
  • SpringBoot应用启动太慢?试试把它编译成Native原生应用
  • 入门指南:基于 CANN 仓库快速理解AI软件栈开发流程
  • AI视角下的 CANN 仓库架构全解析:高效计算的核心
  • 互联网大厂Java求职面试实战:微服务、电商场景与Spring生态详解
  • 用 CANN ops-nn 提升 AI 性能:实操技巧与核心逻辑拆解
  • 优化校园光环境:从照亮空间到专业护眼照明转变
  • 用MonkeyOCR解析复杂PDF
  • CANN 生态新进展:ops-nn 仓库如何赋能大模型训练?
  • USACO历年黄金组真题解析 | 2005年11月
  • 格莱美评审官方认证!吴克群“忠于自我”创作观成国际标杆,他早就该被世界看见!
  • OpenClaw Slack 集成指南
  • 编程大师-技术-算法-leetcode-1472. 设计浏览器历史记录
  • python synonyms库,深度解析
  • 微痕之下,十年追凶——《风过留痕》以痕检视角揭开改编自真实案件的刑侦迷雾
  • PostgreSQL 性能优化:分区表实战
  • python openai库,深度解析
  • PostgreSQL 性能优化:如何安全地终止一个正在执行的大事务?
  • 从好命哥到黑天鹅,黄晓明把东北之旅玩成了喜剧片
  • PostgreSQL性能优化:如何定期清理无用索引以释放磁盘空间(索引膨胀监控)
  • python Flower库,深度解析
  • Python requests 库,深度解析
  • python jieba库,深度解析
  • 第七节:框架版本大升级(CoreMvc10.x + EFCore10.x)
  • C++ 面向控制标记编程(CMOP)到底是什么?一篇讲透这个小众但优雅的范式
  • 完整教程:XILINX SRIOIP核详解、FPGA实现及仿真全流程(Serial RapidIO Gen2 Endpoint v4.1)
  • 探索风力发电MPPT并网模型:策略模块的奇妙世界
  • 思考是用来解决问题和总结经验的,而不是用来制造障碍的:不为打翻的牛奶哭泣底层逻辑是,哭泣仅仅是情绪表达,不是在解决问题,我们应该想的是尽快打扫不要扎到脚