NRF52832笔记(14) 软件模拟I2C
发布日期:2021-06-29 05:35:43 浏览次数:2 分类:技术文章

本文共 13795 字,大约阅读时间需要 45 分钟。

1 i2c理论

1.1 i2c时序图

在这里插入图片描述

启动信号:

  SCL为高电平的时候,SDA由高电平向低电平跳变。
结束信号:
SCL为高电平的时候,SDA由低电平向高电平跳变。
应答信号:
I2C总线上的所有数据都是以8位字节传送的,发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功,对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号P。
写操作:
  开始信号:主机+从设备地址+写命令,从机应答,应答成功,表示有这个设备,然后主机+设备内部寄存器地址,此时不用再加写命令控制字,从机应答,应答成功,表示设备内有这个地址,主机写入数据,从机应答,是否继续发送,不发送的话,发送停止信号P。
读时序:
  要想读设备,首先要知道将要所读取设备的地址告诉从设备,从设备才能将数据放到(发送)SDA上使主设备读取,从设备将数据放入SDA上的过程,由硬件主动完成,不用人为的写入。所以首先先写入从机地址,然后+写控制命令,从机应答,应答成功,表示有这个设备,然后写入内部寄存器地址,此时不用再加写命令控制字,从机应答,应答成功,表示设备内有这个地址。然后主机继续发出:写入从机地址,然后+读命令,从机应答,应答成功,此时便可以读取数据了,从设备已经将数据放入到SDA上了。地址跟设备已经验证了,不用再进行验证
  
数据位传送
进行数据传送时,在SCL呈现高电平期间,SDA上的电平必须保持稳定,低电平为数据0,高电平为数据1。只有在SCL为低电平期间,才允许SDA上的电平改变状态。逻辑0的电平为低电压,而逻辑1的电平取决于器件本身的正电源电压VDD(当使用独立电源时)。数据位的传输是边沿触发。

2 .h

#ifndef IIC_H#define IIC_H#include 
#include
#include "nrf_gpio.h"/*lint -e415 -e845 -save "Out of bounds access" */#define TWI_SDA_STANDARD0_NODRIVE1_HRS() do { \ NRF_GPIO->PIN_CNF[sda_hrs] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) \ |(GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) \ |(GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) \ |(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) \ |(GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos); \} while (0) /*!< Configures SDA pin to Standard-0, No-drive 1 */#define TWI_SCL_STANDARD0_NODRIVE1_HRS() do { \ NRF_GPIO->PIN_CNF[scl_hrs] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) \ |(GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) \ |(GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) \ |(GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) \ |(GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos); \} while (0) /*!< Configures SCL pin to Standard-0, No-drive 1 */#define TWI_SCL_HIGH_HRS() do { NRF_GPIO->OUTSET = (1UL << scl_hrs); } while(0) /*!< Pulls SCL line high */#define TWI_SCL_LOW_HRS() do { NRF_GPIO->OUTCLR = (1UL << scl_hrs); } while(0) /*!< Pulls SCL line low */#define TWI_SDA_HIGH_HRS() do { NRF_GPIO->OUTSET = (1UL << sda_hrs); } while(0) /*!< Pulls SDA line high */#define TWI_SDA_LOW_HRS() do { NRF_GPIO->OUTCLR = (1UL << sda_hrs); } while(0) /*!< Pulls SDA line low */#define TWI_SDA_INPUT_HRS() do { NRF_GPIO->DIRCLR = (1UL << sda_hrs); } while(0) /*!< Configures SDA pin as input */#define TWI_SDA_OUTPUT_HRS() do { NRF_GPIO->DIRSET = (1UL << sda_hrs); } while(0) /*!< Configures SDA pin as output */#define TWI_SCL_OUTPUT_HRS() do { NRF_GPIO->DIRSET = (1UL << scl_hrs); } while(0) /*!< Configures SCL pin as output */#define TWI_SDA_READ_HRS() ((NRF_GPIO->IN >> sda_hrs) & 0x1UL) /*!< Reads current state of SDA */#define TWI_SCL_READ_HRS() ((NRF_GPIO->IN >> scl_hrs) & 0x1UL) /*!< Reads current state of SCL */#define TWI_READ_BIT_HRS (0x01) //!< If this bit is set in the address field, transfer direction is from slave to master.#define TWI_ISSUE_STOP_HRS ((bool)true) //!< Parameter for @ref twi_master_transfer#define TWI_DONT_ISSUE_STOP_HRS ((bool)false) //!< Parameter for @ref twi_master_transfer#define HRS_US 1#define TWI_MASTER_TIMEOUT_COUNTER_LOAD_VALUE_HRS 20//1000#define TWI_ISSUE_STOP ((bool)true) //!< Parameter for @ref twi_master_transfer#define TWI_DONT_ISSUE_STOP ((bool)false) //!< Parameter for @ref twi_master_transferbool hrs_iic_init(uint8_t sda_pin,uint8_t scl_pin);// bool iic_transfer(uint8_t address, uint8_t *data, uint16_t data_length, bool issue_stop_condition);bool hrs_iic_transfer(uint8_t address, uint8_t * data, uint16_t data_length, bool issue_stop_condition);#endif //TWI_MASTER_H

2.1 .c文件

/* Copyright (c) 2009 Nordic Semiconductor. All Rights Reserved. * * The information contained herein is property of Nordic Semiconductor ASA. * Terms and conditions of usage are described in detail in NORDIC * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. * * Licensees are granted free, non-transferable use of the information. NO * WARRANTY of ANY KIND is provided. This heading must NOT be removed from * the file. * */#include "iic_io.h"#include "utility.h"#define  TWI_DELAY_HRS() delay_us(1)#define IIC_DELAY(A)    delay_us((A)*100)uint8_t sda_hrs,scl_hrs;static bool twi_master_clear_bus(void);static bool twi_master_issue_startcondition(void);static bool twi_master_issue_stopcondition(void);static bool twi_master_clock_byte(uint_fast8_t databyte);static bool twi_master_clock_byte_in(uint8_t * databyte, bool ack);static bool twi_master_wait_while_scl_low(void);bool hrs_iic_init(uint8_t sda_pin,uint8_t scl_pin){
// Configure both pins to output Standard 0, No-drive (open-drain) 1 sda_hrs =sda_pin; scl_hrs=scl_pin; TWI_SDA_STANDARD0_NODRIVE1_HRS(); TWI_SCL_STANDARD0_NODRIVE1_HRS(); /*lint !e416 "Creation of out of bounds pointer" */ // Configure SCL as output TWI_SCL_HIGH_HRS(); TWI_SCL_OUTPUT_HRS(); // Configure SDA as output TWI_SDA_HIGH_HRS(); TWI_SDA_OUTPUT_HRS(); return twi_master_clear_bus();}bool hrs_iic_transfer(uint8_t address, uint8_t * data, uint16_t data_length, bool issue_stop_condition){
bool transfer_succeeded = true; transfer_succeeded &= twi_master_issue_startcondition(); transfer_succeeded &= twi_master_clock_byte(address); if (address & TWI_READ_BIT_HRS) {
/* Transfer direction is from Slave to Master */ while (data_length-- && transfer_succeeded) {
// To indicate to slave that we've finished transferring last data byte // we need to NACK the last transfer. if (data_length == 0) {
transfer_succeeded &= twi_master_clock_byte_in(data, (bool)false); } else {
transfer_succeeded &= twi_master_clock_byte_in(data, (bool)true); } data++; } } else {
/* Transfer direction is from Master to Slave */ while (data_length-- && transfer_succeeded) {
transfer_succeeded &= twi_master_clock_byte(*data); data++; } } if (issue_stop_condition || !transfer_succeeded) {
transfer_succeeded &= twi_master_issue_stopcondition(); } return transfer_succeeded;}/** * @brief Function for detecting stuck slaves and tries to clear the bus. * * @return * @retval false Bus is stuck. * @retval true Bus is clear. */static bool twi_master_clear_bus(void){
bool bus_clear; uint_fast8_t i; TWI_SDA_HIGH_HRS(); TWI_SCL_HIGH_HRS(); TWI_DELAY_HRS(); if (TWI_SDA_READ_HRS() == 1 && TWI_SCL_READ_HRS() == 1) {
bus_clear = true; } else if (TWI_SCL_READ_HRS() == 1) {
bus_clear = false; // Clock max 18 pulses worst case scenario(9 for master to send the rest of command and 9 for slave to respond) to SCL line and wait for SDA come high for ( i = 18; i--;) {
TWI_SCL_LOW_HRS(); TWI_DELAY_HRS(); TWI_SCL_HIGH_HRS(); TWI_DELAY_HRS(); if (TWI_SDA_READ_HRS() == 1) {
bus_clear = true; break; } } } else {
bus_clear = false; } return bus_clear;}/** * @brief Function for issuing TWI START condition to the bus. * * START condition is signaled by pulling SDA low while SCL is high. After this function SCL and SDA will be low. * * @return * @retval false Timeout detected * @retval true Clocking succeeded */static bool twi_master_issue_startcondition(void){
// Make sure both SDA and SCL are high before pulling SDA low. TWI_SDA_HIGH_HRS(); TWI_DELAY_HRS(); if (!twi_master_wait_while_scl_low()) {
return false; } TWI_SDA_LOW_HRS(); TWI_DELAY_HRS(); // Other module function expect SCL to be low TWI_SCL_LOW_HRS(); TWI_DELAY_HRS(); return true;}/** * @brief Function for issuing TWI STOP condition to the bus. * * STOP condition is signaled by pulling SDA high while SCL is high. After this function SDA and SCL will be high. * * @return * @retval false Timeout detected * @retval true Clocking succeeded */static bool twi_master_issue_stopcondition(void){
TWI_SDA_LOW_HRS(); TWI_DELAY_HRS(); if (!twi_master_wait_while_scl_low()) {
return false; } TWI_SDA_HIGH_HRS(); TWI_DELAY_HRS(); return true;}/** * @brief Function for clocking one data byte out and reads slave acknowledgment. * * Can handle clock stretching. * After calling this function SCL is low and SDA low/high depending on the * value of LSB of the data byte. * SCL is expected to be output and low when entering this function. * * @param databyte Data byte to clock out. * @return * @retval true Slave acknowledged byte. * @retval false Timeout or slave didn't acknowledge byte. */static bool twi_master_clock_byte(uint_fast8_t databyte){
uint_fast8_t i; bool transfer_succeeded = true; /** @snippet [TWI SW master write] */ // Make sure SDA is an output TWI_SDA_OUTPUT_HRS(); // MSB first for ( i = 0x80; i != 0; i >>= 1) {
TWI_SCL_LOW_HRS(); TWI_DELAY_HRS(); if (databyte & i) {
TWI_SDA_HIGH_HRS(); } else {
TWI_SDA_LOW_HRS(); } if (!twi_master_wait_while_scl_low()) {
transfer_succeeded = false; // Timeout break; } } // Finish last data bit by pulling SCL low TWI_SCL_LOW_HRS(); TWI_DELAY_HRS(); /** @snippet [TWI SW master write] */ // Configure TWI_SDA pin as input for receiving the ACK bit TWI_SDA_INPUT_HRS(); // Give some time for the slave to load the ACK bit on the line TWI_DELAY_HRS(); // Pull SCL high and wait a moment for SDA line to settle // Make sure slave is not stretching the clock transfer_succeeded &= twi_master_wait_while_scl_low(); // Read ACK/NACK. NACK == 1, ACK == 0 transfer_succeeded &= !(TWI_SDA_READ_HRS()); // Finish ACK/NACK bit clock cycle and give slave a moment to release control // of the SDA line TWI_SCL_LOW_HRS(); TWI_DELAY_HRS(); // Configure TWI_SDA pin as output as other module functions expect that TWI_SDA_OUTPUT_HRS(); return transfer_succeeded;}/** * @brief Function for clocking one data byte in and sends ACK/NACK bit. * * Can handle clock stretching. * SCL is expected to be output and low when entering this function. * After calling this function, SCL is high and SDA low/high depending if ACK/NACK was sent. * * @param databyte Data byte to clock out. * @param ack If true, send ACK. Otherwise send NACK. * @return * @retval true Byte read succesfully * @retval false Timeout detected */static bool twi_master_clock_byte_in(uint8_t *databyte, bool ack){
uint_fast8_t i; uint_fast8_t byte_read = 0; bool transfer_succeeded = true; /** @snippet [TWI SW master read] */ // Make sure SDA is an input TWI_SDA_INPUT_HRS(); // SCL state is guaranteed to be high here // MSB first for ( i = 0x80; i != 0; i >>= 1) {
if (!twi_master_wait_while_scl_low()) {
transfer_succeeded = false; break; } if (TWI_SDA_READ_HRS()) {
byte_read |= i; } else {
// No need to do anything } TWI_SCL_LOW_HRS(); TWI_DELAY_HRS(); } // Make sure SDA is an output before we exit the function TWI_SDA_OUTPUT_HRS(); /** @snippet [TWI SW master read] */ *databyte = (uint8_t)byte_read; // Send ACK bit // SDA high == NACK, SDA low == ACK if (ack) {
TWI_SDA_LOW_HRS(); } else {
TWI_SDA_HIGH_HRS(); } // Let SDA line settle for a moment TWI_DELAY_HRS(); // Drive SCL high to start ACK/NACK bit transfer // Wait until SCL is high, or timeout occurs if (!twi_master_wait_while_scl_low()) {
transfer_succeeded = false; // Timeout } // Finish ACK/NACK bit clock cycle and give slave a moment to react TWI_SCL_LOW_HRS(); TWI_DELAY_HRS(); return transfer_succeeded;}#include "nrf_delay.h"/** * @brief Function for pulling SCL high and waits until it is high or timeout occurs. * * SCL is expected to be output before entering this function. * @note If TWI_MASTER_TIMEOUT_COUNTER_LOAD_VALUE is set to zero, timeout functionality is not compiled in. * @return * @retval true SCL is now high. * @retval false Timeout occurred and SCL is still low. */static bool twi_master_wait_while_scl_low(void){
#if TWI_MASTER_TIMEOUT_COUNTER_LOAD_VALUE_HRS != 0 uint32_t volatile timeout_counter = TWI_MASTER_TIMEOUT_COUNTER_LOAD_VALUE_HRS;#endif // Pull SCL high just in case if something left it low TWI_SCL_HIGH_HRS(); TWI_DELAY_HRS(); while (TWI_SCL_READ_HRS() == 0) {
// If SCL is low, one of the slaves is busy and we must wait #if TWI_MASTER_TIMEOUT_COUNTER_LOAD_VALUE_HRS != 0 IIC_DELAY(20); // TWI_DELAY_HRS(); if (timeout_counter-- == 0) {
// If timeout_detected, return false return false; }#endif } return true;}/*lint --flb "Leave library region" */

3 utility的文件

主要是延时函数 .c文件

#include "utility.h"void delay_us(uint32_t us){
uint32_t i,j; for(j=0; j

.h文件

#ifndef UTILITY_H___#define UTILITY_H___#include "stdint.h"//#include "nrf52.h"void delay_us(uint32_t us);void delay_ms(uint32_t ms);#endif

可以加入QQ群:687360507

与大伙沟通交流,技术在于分享而进步

转载地址:https://blog.csdn.net/zhi_Alanwu/article/details/105577263 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:NRF52832笔记(15)keil 仿真查看变量实时变化
下一篇:NRF52832笔记(13)软件模拟spi 4种模式

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年04月27日 14时12分14秒