本帖最后由 xiaoqi976633690 于 2023-12-6 19:55 编辑
一、项目描述 1、项目介绍 沁恒的蓝牙mcu还是很有性价比的,自己买了个芯片CH582F
嘉立创做了个小板子来学习蓝牙通信。并记录下来。 2、设计思路 本次设计思路比较的简单 a.通信部分是蓝牙ble5.0通信协议 b.TMOS 系统的任务和事件的创建 c.I2C读写实现(硬件搞半天没成功,直接用软件I2C) d.温度传感器AHT30 通过tmos将这些模块组合起来使用 3、硬件 CH582F最小系统开发板+AHT30
二、软件流程图及各功能对应的主要代码片段及说明 介绍 1、流程图
2、代码片段说明 软件i2c实现 #include "I2C.h"
//模拟IIC初始化
void I2CInit(void)
{
SCL_out;
SDA_out;
}
void I2C_delay(void)
{
mDelayuS(100);
}
int I2C_Start(void)
{
SDA_H;
SDA_out;
SCL_H;
I2C_delay();
if(!SDA_read)return 0; //SDA线为低电平则总线忙,退出
SDA_L;
I2C_delay();
if(SDA_read) return 0; //SDA线为高电平则总线出错,退出
SDA_L;
I2C_delay();
return 1;
}
void I2C_Stop(void)
{
SCL_L;
I2C_delay();
SDA_L;
SDA_out;
I2C_delay();
SCL_H;
I2C_delay();
SDA_H;
I2C_delay();
}
void I2C_Ack(void)
{
SCL_L;
I2C_delay();
SDA_L;
SDA_out;
I2C_delay();
SCL_H;
I2C_delay();
I2C_delay();
SCL_L;
I2C_delay();
}
void I2C_NoAck(void)
{
SCL_L;
I2C_delay();
SDA_H;
SDA_out;
I2C_delay();
SCL_H;
I2C_delay();
SCL_L;
I2C_delay();
}
int I2C_WaitAck(void) //返回为:=1有ACK,=0无ACK
{
SCL_L;
I2C_delay();
I2C_delay();
SDA_in;
SCL_H;
I2C_delay();
I2C_delay();
if(SDA_read)
{
SCL_L;
return 0;
}
SCL_L;
return 1;
}
void I2C_SendByte(u8 SendByte) //数据从高位到低位//
{
u8 i=8;
SDA_out;
while(i--)
{
SCL_L;
I2C_delay();
if(SendByte&0x80)
SDA_H;
else
SDA_L;
SendByte<<=1;
I2C_delay();
SCL_H;
I2C_delay();
I2C_delay();
}
SCL_L;
}
u8 I2C_ReadByte(void) //数据从高位到低位//
{
u8 i=8;
u8 ReceiveByte=0;
SDA_H;
SDA_out;
while(i--)
{
ReceiveByte<<=1;
SCL_L;
I2C_delay();
I2C_delay();
SCL_H;
SDA_in;
I2C_delay();
I2C_delay();
if(SDA_read)
{
ReceiveByte|=0x01;
}
}
SCL_L;
return ReceiveByte;
}
//7bit地址单字节写入*******************************************
int I2C_7bit_Single_Write(u8 SlaveAddress, u8 REG_data)
{
if(!I2C_Start())return 0;
I2C_SendByte(SlaveAddress); //发送设备地址+写信号//I2C_SendByte(((REG_Address & 0x0700) >>7) | SlaveAddress & 0xFFFE);//设置高起始地址+器件地址
if(!I2C_WaitAck()){I2C_Stop(); return 0;}
I2C_SendByte(REG_data);
I2C_WaitAck(); I2C_Stop();
return 1;
}
//7bit地址单字节读取*****************************************
u8 I2C_7bit_Single_Read(u8 SlaveAddress)
{
unsigned char REG_data;
if(!I2C_Start())return 0;
I2C_SendByte(SlaveAddress); //I2C_SendByte(((REG_Address & 0x0700) >>7) | REG_Address & 0xFFFE);//设置高起始地址+器件地址
if(!I2C_WaitAck())
{
I2C_Stop();
return 0;
}
I2C_Start();
I2C_SendByte(SlaveAddress+1);
I2C_WaitAck();
REG_data= I2C_ReadByte();
I2C_NoAck();
I2C_Stop();
//return TRUE;
return REG_data;
}
//7bit地址多字节读取*****************************************
int I2C_7bit_Mult_Read(u8 SlaveAddress,u8 * ptChar,u8 size)
{
u8 i;
if(size < 1)
return 0;
if(!I2C_Start())
return 0;
I2C_SendByte(SlaveAddress);
if(!I2C_WaitAck())
{
I2C_Stop();
return 0;
}
I2C_Start();
I2C_SendByte(SlaveAddress+1);
I2C_WaitAck();
for(i=1;i<size; i++)
{
*ptChar++ = I2C_ReadByte();
I2C_Ack();
}
*ptChar++ = I2C_ReadByte();
I2C_NoAck();
I2C_Stop();
return 1;
}
//10bit地址单字节写入*******************************************
int I2C_10bit_Single_Write(u8 SlaveAddress,u8 REG_Address,u8 REG_data)
{
if(!I2C_Start())return 0;
I2C_SendByte(SlaveAddress); //发送设备地址+写信号//I2C_SendByte(((REG_Address & 0x0700) >>7) | SlaveAddress & 0xFFFE);//设置高起始地址+器件地址
if(!I2C_WaitAck()){I2C_Stop(); return 0;}
I2C_SendByte(REG_Address ); //设置低起始地址
I2C_WaitAck();
I2C_SendByte(REG_data);
I2C_WaitAck();
I2C_Stop();
return 1;
}
//10bit地址单字节读取*****************************************
u8 I2C_10bit_Single_Read(u8 SlaveAddress,u8 REG_Address)
{
unsigned char REG_data;
if(!I2C_Start())return 0;
I2C_SendByte(SlaveAddress); //I2C_SendByte(((REG_Address & 0x0700) >>7) | REG_Address & 0xFFFE);//设置高起始地址+器件地址
if(!I2C_WaitAck())
{
I2C_Stop();
return 0;
}
I2C_SendByte((u8) REG_Address); //设置低起始地址
I2C_WaitAck();
I2C_Start();
I2C_SendByte(SlaveAddress+1);
I2C_WaitAck();
REG_data= I2C_ReadByte();
I2C_NoAck();
I2C_Stop();
//return TRUE;
return REG_data;
}
//10bit地址多字节读取*****************************************
int I2C_10bit_Mult_Read(u8 SlaveAddress,u8 REG_Address,u8 * ptChar,u8 size)
{
u8 i;
if(size < 1)
return 0;
if(!I2C_Start())
return 0;
I2C_SendByte(SlaveAddress);
if(!I2C_WaitAck())
{
I2C_Stop();
return 0;
}
I2C_SendByte(REG_Address);
I2C_WaitAck();
I2C_Start();
I2C_SendByte(SlaveAddress+1);
I2C_WaitAck();
for(i=1;i<size; i++)
{
*ptChar++ = I2C_ReadByte();
I2C_Ack();
}
*ptChar++ = I2C_ReadByte();
I2C_NoAck();
I2C_Stop();
return 1;
}
/********************************** (C) COPYRIGHT *******************************
* File Name : I2C.h
* Author : WCH
* Version : V1.0
* Date : 2018/12/15
* Description :
*******************************************************************************/
#ifndef __I2C_SOFT2_H__
#define __I2C_SOFT2_H__
#include "CH58x_common.h"
#ifndef UINT8
typedef unsigned char u8;
#endif
#define I2C_Pin_SCL GPIO_Pin_13
#define I2C_Pin_SDA GPIO_Pin_12
#define SCL_H GPIOB_SetBits( I2C_Pin_SCL )
#define SCL_L GPIOB_ResetBits( I2C_Pin_SCL )
#define SDA_H GPIOB_SetBits( I2C_Pin_SDA )
#define SDA_L GPIOB_ResetBits( I2C_Pin_SDA )
#define SCL_read GPIOB_ReadPortPin( I2C_Pin_SCL )
#define SDA_read GPIOB_ReadPortPin( I2C_Pin_SDA )
#define SCL_out GPIOB_ModeCfg(I2C_Pin_SCL, GPIO_ModeOut_PP_5mA)
#define SCL_in GPIOB_ModeCfg(I2C_Pin_SCL, GPIO_ModeIN_PU)
#define SDA_out GPIOB_ModeCfg(I2C_Pin_SDA, GPIO_ModeOut_PP_5mA)
#define SDA_in GPIOB_ModeCfg(I2C_Pin_SDA, GPIO_ModeIN_PU)
void I2CInit(void);
int I2C_7bit_Single_Write(u8 SlaveAddress, u8 REG_data);
u8 I2C_7bit_Single_Read(u8 Slave7BitAddress);
int I2C_7bit_Mult_Read(u8 Slave7BitAddress,u8 * ptChar,u8 size);
int I2C_10bit_Single_Write(u8 SlaveAddress,u8 REG_Address,u8 REG_data);
u8 I2C_10bit_Single_Read(u8 SlaveAddress,u8 REG_Address);
int I2C_10bit_Mult_Read(u8 SlaveAddress,u8 REG_Address,u8 * ptChar,u8 size);
void I2C_delay(void);
int I2C_Start(void);
void I2C_Stop(void);
void I2C_Ack(void);
void I2C_NoAck(void);
int I2C_WaitAck(void); //返回为:=1有ACK,=0无ACK
void I2C_SendByte(u8 SendByte);
u8 I2C_ReadByte(void); //数据从高位到低位//
#endif
AHT30初始化 void Init_AHTx0(uint8_t address)
{
uint8_t com[3]={0xBE,0x08,0x00};
I2C_Write_Byte(address,com,3);
printf("Init_AHTx0 OK\n");
DelayMs(100);
}
AHT30读写函数 uint8_t I2C_Write_Byte(uint8_t addr_7bit ,uint8_t * data, uint8_t len)
{
uint8_t i=0;
if(!I2C_Start())return 0;
I2C_SendByte(addr_7bit<<1);
if(!I2C_WaitAck()){I2C_Stop(); return 0;}
for(i=0;i<len;i++)
{
I2C_SendByte(data[i]);
I2C_WaitAck();
}
I2C_Stop();
return 1;
}
uint8_t I2C_Read_Byte(uint8_t addr_7bit, uint8_t *data, uint8_t len)
{
uint8_t i=0;
if(!I2C_Start())return 0;
I2C_SendByte((addr_7bit<<1)|0x01); //bit7:1 为读
if(!I2C_WaitAck()){I2C_Stop(); return 0;}
while(i<(len-1))
{
data[i]= I2C_ReadByte();
I2C_Ack();
i++;
}
data[i]= I2C_ReadByte();
I2C_NoAck();
I2C_Stop();
return 1;
}
1.上电后要等待40ms,读取温湿度值之前, 首先要看状态字的校准使能位Bit[3]是否为 1(通过发送0x71可以获取一个字节的状态字),如果不为1,要发送0xBE命令(初始化),此命令参数有两个字节, 第一个字节为0x08,第二个字节为0x00。 uint8_t com_init[3]={0xbe,0x08,0x00};//0xbe
2.直接发送 0xAC命令(触发测量),此命令参数有两个字节,第一个字节为 0x33,第二个字节为0x00。
3.等待75ms待测量完成,忙状态Bit[7]为0,然后可以读取六个字节(发0X71即可以读取)。 4.计算温湿度值。 #define buff_size 6
uint8_t com_read[3]={0xac,0x33,0x00};//0xac
uint8_t temprature_buff[buff_size]={0x00,0x00,0x00,0x00,0x00,0x00};
uint8_t t[20];
void Get_tmprature(void)
{
I2C_Write_Byte(0x38,com_read,3);//开始测量
DelayMs(150);
I2C_Read_Byte(0x38,temprature_buff,buff_size);//读取温湿度
Temp_Huimt(temprature_buff,t);
}
void Temp_Huimt(uint8_t * arr,uint8_t * t)
{
int32_t temp;
int32_t humit;
humit =arr[1];
humit=((humit)<<8)|arr[2];
humit=((humit)<<4)|(arr[3]>>4);
temp =arr[3]&0x0f;
temp=((temp)<<8)|arr[4];
temp=((temp)<<8)|arr[5];
sprintf(t,"h=%.2f,t=%.2f\n\r",(float)humit*100/1048576,(float)temp*200/1048576-50);
}
蓝牙部分 创建server 创建特性 <blockquote>#define SIMPLEPROFILE_CHAR1 0 // RW uint8_t - Profile Characteristic 1 value
// Simple Profile Characteristic 4 Properties
static uint8_t simpleProfileChar4Props = GATT_PROP_NOTIFY;
// Characteristic 4 Value
static uint8_t simpleProfileChar4[SIMPLEPROFILE_CHAR4_LEN] = {0};
// Simple Profile Characteristic 4 Configuration Each client has its own
// instantiation of the Client Characteristic Configuration. Reads of the
// Client Characteristic Configuration only shows the configuration for
// that client and writes only affect the configuration of that client.
static gattCharCfg_t simpleProfileChar4Config[PERIPHERAL_MAX_CONNECTION];
// Simple Profile Characteristic 4 User Description
static uint8_t simpleProfileChar4UserDesp[] = "Characteristic 4\0";
添加server服务 SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile
初始化蓝牙 void Peripheral_Init()
{
//将事件的回调函数注册到 TMOS 中
Peripheral_TaskID = TMOS_ProcessEventRegister(Peripheral_ProcessEvent);
// Setup the GAP Peripheral Role Profile
{
uint8_t initial_advertising_enable = TRUE;
uint16_t desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
uint16_t desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;
// Set the GAP Role Parameters
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable);//使能广播
GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData);//扫描回复数据,数据格式要遵循广播数据格式
GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);//扫描回复数据,数据格式要遵循广播数据格式
GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t), &desired_min_interval);//最小连接间隔
GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t), &desired_max_interval);//最大连接间隔
}
// Set the GAP Characteristics
GGS_SetParameter(GGS_DEVICE_NAME_ATT, sizeof(attDeviceName), attDeviceName);//GAP特征,设备名称
{
uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL;
// Set advertising interval
GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, advInt);
GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, advInt);
// Enable scan req notify
GAP_SetParamValue(TGAP_ADV_SCAN_REQ_NOTIFY, ENABLE);
}
// Setup the GAP Bond Manager
{
uint32_t passkey = 0; // passkey "000000"
uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
uint8_t mitm = TRUE;
uint8_t bonding = TRUE;
uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
GAPBondMgr_SetParameter(GAPBOND_PERI_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey);
GAPBondMgr_SetParameter(GAPBOND_PERI_PAIRING_MODE, sizeof(uint8_t), &pairMode);
GAPBondMgr_SetParameter(GAPBOND_PERI_MITM_PROTECTION, sizeof(uint8_t), &mitm);
GAPBondMgr_SetParameter(GAPBOND_PERI_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
GAPBondMgr_SetParameter(GAPBOND_PERI_BONDING_ENABLED, sizeof(uint8_t), &bonding);
}
// Initialize GATT attributes
// GGS_AddService(GATT_ALL_SERVICES); // GAP
// GATTServApp_AddService(GATT_ALL_SERVICES); // GATT attributes
// DevInfo_AddService(); // Device Information Service
SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile
// Setup the SimpleProfile Characteristic Values
{
uint8_t charValue1[SIMPLEPROFILE_CHAR1_LEN] = {1};
uint8_t charValue2[SIMPLEPROFILE_CHAR2_LEN] = {2};
uint8_t charValue3[SIMPLEPROFILE_CHAR3_LEN] = {3};
uint8_t charValue4[SIMPLEPROFILE_CHAR4_LEN] = {4};
uint8_t charValue5[SIMPLEPROFILE_CHAR5_LEN] = {1, 2, 3, 4, 5};
uint8_t charValue6[SIMPLEPROFILE_CHAR6_LEN] = {'X','Q','-','B','L','E',0x0a};
SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1, SIMPLEPROFILE_CHAR1_LEN, charValue1);
SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR2, SIMPLEPROFILE_CHAR2_LEN, charValue2);
SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR3, SIMPLEPROFILE_CHAR3_LEN, charValue3);
SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, SIMPLEPROFILE_CHAR4_LEN, charValue4);
SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5);
SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR6, SIMPLEPROFILE_CHAR6_LEN, charValue6);
}
// Init Connection Item
peripheralInitConnItem(&peripheralConnList);
// Register callback with SimpleGATTprofile
SimpleProfile_RegisterAppCBs(&Peripheral_SimpleProfileCBs);
// Register receive scan request callback
GAPRole_BroadcasterSetCB(&Broadcaster_BroadcasterCBs);
// Setup a delayed profile startup
tmos_set_event(Peripheral_TaskID, SBP_START_DEVICE_EVT); //启动Peripheral_TaskID任务下的SBP_START_DEVICE_EVT事件,其中tmos_set_event函数用于立刻执行某个事件
tmos_start_reload_task(Peripheral_TaskID, TASK_1,3200);//启动一个循环事件
tmos_start_reload_task(Peripheral_TaskID,TASK_TEMPRATURE,2200);//启动一个温湿度循环事件
}
事件轮询 uint16_t Peripheral_ProcessEvent(uint8_t task_id, uint16_t events)
{
// VOID task_id; // TMOS required parameter that isn't used in this function
if(events & SYS_EVENT_MSG)
{
uint8_t *pMsg;
if((pMsg = tmos_msg_receive(Peripheral_TaskID)) != NULL)
{
Peripheral_ProcessTMOSMsg((tmos_event_hdr_t *)pMsg);
// Release the TMOS message
tmos_msg_deallocate(pMsg);
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
if(events & SBP_START_DEVICE_EVT)
{
// Start the Device
GAPRole_PeripheralStartDevice(Peripheral_TaskID, &Peripheral_BondMgrCBs, &Peripheral_PeripheralCBs);
return (events ^ SBP_START_DEVICE_EVT);
}
if(events & SBP_PERIODIC_EVT)
{
// Restart timer
if(SBP_PERIODIC_EVT_PERIOD)
{
tmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD);
}
// Perform periodic application task
performPeriodicTask();
return (events ^ SBP_PERIODIC_EVT);
}
if(events & SBP_PARAM_UPDATE_EVT)
{
// Send connect param update request
GAPRole_PeripheralConnParamUpdateReq(peripheralConnList.connHandle,
DEFAULT_DESIRED_MIN_CONN_INTERVAL,
DEFAULT_DESIRED_MAX_CONN_INTERVAL,
DEFAULT_DESIRED_SLAVE_LATENCY,
DEFAULT_DESIRED_CONN_TIMEOUT,
Peripheral_TaskID);
return (events ^ SBP_PARAM_UPDATE_EVT);
}
if(events & SBP_PHY_UPDATE_EVT)
{
// start phy update
PRINT("PHY Update %x...\n", GAPRole_UpdatePHY(peripheralConnList.connHandle, 0,
GAP_PHY_BIT_LE_2M, GAP_PHY_BIT_LE_2M, GAP_PHY_OPTIONS_NOPRE));
return (events ^ SBP_PHY_UPDATE_EVT);
}
if(events & SBP_READ_RSSI_EVT)
{
GAPRole_ReadRssiCmd(peripheralConnList.connHandle);
tmos_start_task(Peripheral_TaskID, SBP_READ_RSSI_EVT, SBP_READ_RSSI_EVT_PERIOD);
return (events ^ SBP_READ_RSSI_EVT);
}
if(events & TASK_1)
{
Get_temp();
return (events ^ TASK_1);
}
if(events & TASK_TEMPRATURE)
{
Get_tmprature();
Temp_Huimt(temprature_buff,t);
printf("%s\n",t);
return (events ^ TASK_TEMPRATURE);
}
// Discard unknown events
return 0;
}
三、功能展示及说明 手机链接蓝牙
|