一、前言
此片文章主要介绍如果通过STM32系列开发板来读取TOF250(IIC接口)数据的教程
提示:以下的案例仅供参考学习使用
二、硬件准备
-
TOF250 :测距传感器
-
STM32系列开发板:STM32F103ZE 主频8M
提示:STM32系列开发板种类较多,此次测试使用的是普中科技的PZ6806L开发板,其他类型STM32开发板程序可能会有略微差异
详细资料可以参考:STC 官网
-
电脑 :Windows 7/10/11
-
杜邦线:各类(若干)
-
TOF250尾线:购买时自带有一根尾线
二、软件准备
- KEIL IDE:参考官网下载安装教程
- 烧录软件:PZ-ISP V1.86 普中科技
三、硬件接线图
说明:
- 此次测试仅使用的是模拟IIC,方便理解
- USB打印结果需要用到串口1的TX
四、例程源码
main.c
/**************************************************************
**技术论坛:https://blog.csdn.net/HCJ_Application/article/details/124058266
**修改日期:2022/4/27
**技术说明:基于STM32F103ZET6单片机,主频8M
**版本:V1.0
**作者:深圳市弘成基科技有限公司
**************************************************************/
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "myiic.h"
#define TOF250_DIST_H 0x00 //TOF250距离值高8位
#define TOF250_DIST_L 0x01 //TOF250距离值低8位
#define TOF250_READ_L 2 //TOF250读取的长度
#define TOF250_ID 0x52 //TOF250的IIC地址
unsigned char RxData[10]; //IIC接口接收数据区
unsigned int Tof250_dist = 0; //读取到TOF250的具体数值
int main(void)
{
//unsigned char y;
__I2C_Status_TypeDef i2CStatus;
// u16 len; //检测每次接收到的长度,方便字符串转整数的时候进行处理
User_I2C_Init(); //IIC初始化
delay_init();
// NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
/**************************************************************
**TOF250串口通信协议:
**波特率 : 9600
**数据位 : 8
**校验 : 无
**停止位 : 1
****************************************************************/
uart_init(9600); //串口初始化为115200
while(1)
{
i2CStatus = I2C_RecvBytes(TOF250_ID,TOF250_DIST_H, &RxData[0], 2,100); //通过IIC读取TOF250距离值信息
if(i2CStatus == I2C_OK) {
Tof250_dist = RxData[1] + RxData[0]*256;
printf("%u cm\n",Tof250_dist);
}
delay_ms(100); //延时100ms,TOF250默认更新速度10HZ
}
}
usart.h
#ifndef __USART_H
#define __USART_H
#include "stdio.h"
#include "sys.h"
#define USART_REC_LEN 200 //定义最大接收字节数 200
#define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收
extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART_RX_STA; //接收状态标记
//如果想串口中断接收,请不要注释以下宏定义
void uart_init(u32 bound);
#endif
usart.c
#include "sys.h"
#include "usart.h"
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
#if EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记
void uart_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART1, ENABLE); //使能串口1
}
myiic.c
#include "myiic.h"
/*************************************************
Function: User_I2C_Init
Description: config I2C
*************************************************/
void User_I2C_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); //使能GPIOB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); //PB6,PB7 输出高
}
/*************************************************
Function: delay
Description:
Input: Delaycnt - delay cnt
*************************************************/
void delay(uint32_t Delaycnt)
{
uint32_t i;
for(i = 0; i<Delaycnt; i++)
{
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
}
}
/*************************************************
Function: I2C_GenerateStart
Description:
*************************************************/
static void I2C_GenerateStart(void)
{
SDA_OUT(); //sda线输出
IIC_SDA=1;
IIC_SCL=1;
delay(5);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low
delay(5);
IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
delay(5);
}
/*************************************************
Function: I2C_GenerateStop
Description:
*************************************************/
static void I2C_GenerateStop(void)
{
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
delay(5);
IIC_SCL=1;
delay(5);
IIC_SDA=1;
delay(5);
}
/*************************************************
Function: I2C_GenerateAck
Description:
*************************************************/
static void I2C_GenerateAck(__I2C_ACK_TypeDef ack)
{
if(ack == I2C_NACK)
{
IIC_SDA=1;
}
else
{
IIC_SDA=0;;
}
delay(5);
IIC_SCL=1;
delay(5);
IIC_SCL=0;
delay(5);
}
/*************************************************
Function: I2C_RecvAck
Description:
*************************************************/
static __I2C_ACK_TypeDef I2C_RecvAck(uint32_t Timeout)
{
uint32_t timeout = 0;
__I2C_ACK_TypeDef ackStatus;
IIC_SDA=1;
SDA_IN();
delay(5);
IIC_SCL=1;
delay(5);
while(1)
{
ackStatus = (READ_SDA == Bit_RESET) ? I2C_ACK : I2C_NACK;
if(ackStatus == I2C_ACK)
{
break;
}
if((timeout++) > Timeout)
{
ackStatus = I2C_NACK;
break;
}
}
IIC_SCL=0;
delay(5);
//SDA_OUT();
return ackStatus;
}
/*************************************************
Function: I2C_SendByte
Description:
*************************************************/
static __I2C_ACK_TypeDef I2C_SendByte(uint8_t data, uint32_t Timeout)
{
uint8_t i;
IIC_SCL=0;
for (i = 0; i < 8; i++)
{
SDA_OUT();
if((data << i)&0x80)
{
IIC_SDA=1;
}
else
{
IIC_SDA=0;;
}
delay(5);
IIC_SCL=1;
delay(5);
IIC_SCL=0;
delay(5);
}
return I2C_RecvAck(Timeout);
}
/*************************************************
Function: I2C_RecvByte
Description:
*************************************************/
static uint8_t I2C_RecvByte(void)
{
uint8_t i;
uint8_t data = 0, BitStatus = 0;
IIC_SDA=1;
SDA_IN();
delay(5);
for (i = 0; i < 8; i++)
{
data <<= 1;
IIC_SCL=1;
delay(5);
BitStatus = (READ_SDA == Bit_SET) ? 1 : 0;
data |= BitStatus;
IIC_SCL=0;
delay(5);
}
SDA_OUT();
return data;
}
/*************************************************
Function: I2C_DummyWrite
Description: I2C DummyWrite
*************************************************/
static __I2C_Status_TypeDef I2C_DummyWrite(uint8_t SlaveAddr, uint8_t RegAddr, uint8_t OperLen, uint32_t Timeout)
{
/* Generate Start Sign */
I2C_GenerateStart();
/* Send Slave Address */
if(I2C_SendByte((SlaveAddr<<1) | I2C_Transmitter, Timeout) != I2C_ACK)
{
I2C_GenerateStop();
return I2C_ERROR;
}
// /* Send RegAddr_H */
// if(I2C_SendByte((RegAddr >> 8) & 0xFF, Timeout) != I2C_ACK)
// {
// I2C_GenerateStop();
// return I2C_ERROR;
// }
// /* Send RegAddr_L */
// if(I2C_SendByte(RegAddr & 0xFF, Timeout) != I2C_ACK)
// {
// I2C_GenerateStop();
// return I2C_ERROR;
// }
//
/* Send RegAddr */
if(I2C_SendByte(RegAddr , Timeout) != I2C_ACK)
{
I2C_GenerateStop();
return I2C_ERROR;
}
// /* Send Data length */
// if(I2C_SendByte(OperLen, Timeout) != I2C_ACK)
// {
// I2C_GenerateStop();
// return I2C_ERROR;
// }
return I2C_OK;
}
/*************************************************
Function: I2C_RecvBytes
Description: I2C SendBytes
*************************************************/
__I2C_Status_TypeDef I2C_RecvBytes(uint8_t SlaveAddr, uint8_t RegAddr, uint8_t *Rxbuf, uint8_t OperLen, uint32_t Timeout)
{
uint8_t i = 0;
/* Dummy Write */
if(I2C_DummyWrite(SlaveAddr, RegAddr, OperLen, Timeout) != I2C_OK)
{
I2C_GenerateStop();
return I2C_ERROR;
}
/* Generate Start Sign */
I2C_GenerateStart();
/* Send Slave Address */
if(I2C_SendByte((SlaveAddr<<1) | I2C_Receiver, Timeout) != I2C_ACK)
{
I2C_GenerateStop();
return I2C_ERROR;
}
/* Receive Data */
for(i = 0; i < OperLen - 1; i++)
{
Rxbuf[i] = I2C_RecvByte();
I2C_GenerateAck(I2C_ACK);
}
Rxbuf[OperLen - 1] = I2C_RecvByte();
I2C_GenerateAck(I2C_NACK);
/* Generate Stop Sign */
I2C_GenerateStop();
return I2C_OK;
}
myiic.h
#ifndef __MYIIC_H
#define __MYIIC_H
#include "sys.h"
#define USER_I2C_SCL_PIN GPIO_Pin_6
#define USER_I2C_SDA_PIN GPIO_Pin_7
#define USER_I2C_GPIO GPIOB
//IO方向设置
#define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
//IO操作函数
#define IIC_SCL PBout(6) //SCL
#define IIC_SDA PBout(7) //SDA
#define READ_SDA PBin(7) //输入SDA
typedef enum
{
I2C_OK = 0U,
I2C_BUSY = 1U,
I2C_TIMEOUT = 3U,
I2C_ERROR = 4U,
}__I2C_Status_TypeDef;
typedef enum
{
I2C_ACK = 0U,
I2C_NACK = 1U,
}__I2C_ACK_TypeDef;
typedef enum
{
I2C_Transmitter = 0U,
I2C_Receiver = 1U,
}__I2C_Oper_TypeDef;
typedef enum
{
I2C_SDA_IN = 0U,
I2C_SDA_OUT = 1U,
}__I2C_SDAMode_TypeDef;
/*************************************************
Function: User_I2C_Init
Description: config I2C
*************************************************/
void User_I2C_Init(void);
/*************************************************
Function: delay
Description:
Input: Delaycnt - delay cnt
*************************************************/
void delay(uint32_t Delaycnt);
/*************************************************
Function: I2C_SendBytes
Description: I2C SendBytes
*************************************************/
__I2C_Status_TypeDef I2C_SendBytes(uint8_t SlaveAddr, uint8_t RegAddr, uint8_t *TxBuf, uint8_t OperLen, uint32_t Timeout);
/*************************************************
Function: I2C_RecvBytes
Description: I2C SendBytes
*************************************************/
__I2C_Status_TypeDef I2C_RecvBytes(uint8_t SlaveAddr, uint8_t RegAddr, uint8_t *Rxbuf, uint8_t OperLen, uint32_t Timeout);
unsigned char tof250_i2c_read(unsigned char tof_ID,unsigned char word_adress, unsigned char *rdata, unsigned char num);
#endif
五、烧录说明
5.1 烧录接线示意图
5.2 烧录动态图
六、结果输出
通过SSCOM串口软件打印结果(其他串口工具也可以)
提示:注意选择对应的波特率,此次测试使用波特率为9600