初始版本

This commit is contained in:
xiaozhengsheng
2025-08-19 09:49:41 +08:00
parent 10f1ddf1c1
commit 6df0f7d96e
2974 changed files with 1712873 additions and 54 deletions

108
app/Inc/IoControl.h Normal file
View File

@@ -0,0 +1,108 @@
/********************************************************************
Copyright (c) 2021 Xiangyu Medical Co.Ltd. All rights reserved.
FileName : IoControl.h
Author : zhangdawei
Version : V1.0
Date :
Note :
History :
********************************************************************/
#ifndef _IOCONTROL_H__
#define _IOCONTROL_H__
/* Includes ------------------------------------------------------*/
#include "nrf52.h"
#include "nrf_gpio.h"
#include "timer.h"
#include "drv_saadc.h"
#include "drv_uart.h"
#include "nrf_drv_gpiote.h"
//Log需要引用的头文件
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "nrf_delay.h"
/* Public define -------------------------------------------------*/
#define MERGE(Msb,Lsb) ((Msb<<8)|Lsb)
/* 按键检测 */
#define KEY_PWR_SWITICH ( 16 )
/* 电源锁定 */
#define KEY_POWER ( 13 )
/* 指示灯 */
#define LED_YELLOW ( 11 )
#define LED_WHITE ( 12 )
/* DAC 引脚*/
#define PWM_DAC_GPIO ( 26 )
/* 充电芯片引脚定义,低电平是充电中,高电平是充电完成或未充电 */
#define CHG_MANAGER_EN ( 15 ) //充电管理芯片的EN引脚内部200K下拉电阻。 高电平:禁用充电器 低电平Low或悬空Floating启用充电器
#define CHG_MANAGER_PPR ( 23 ) //充电管理芯片的PPR引脚 开漏输出外部上拉3.0V,低电平:输入充电电压正常,高电平:输入充电电压异常
#define CHG_MANAGER_CHG ( 14 ) //充电指示引脚,低电平是充电中,充电结束为高电平
#define STIM_RMS_RELAY_PIN ( 27 ) // 肌电采集继电器控制引脚
#define STIM_RELAY_PIN ( 28 ) //刺激继电器控制引脚
#define SAMPLE_POWER_PIN ( 20 ) //采样电源控制引脚
#define BOOST_DISCHARGE_PIN ( 18 ) //boost升压放电引脚
#define BASE_WAVE_06_PIN ( 6 ) //刺激基础波形控制引脚6
#define BASE_WAVE_07_PIN ( 7 ) //刺激基础波形控制引脚7
#define BASE_WAVE_08_PIN ( 8 ) //刺激基础波形控制引脚8
#define BOOST_VOLTAGE_CONTROL_PIN ( 25 ) //boost电压控制引脚
/* Public typedef ------------------------------------------------*/
//设备开关机状态
typedef enum
{
POWER_OPEN = 0,
POWER_CLOSE
}DeviceStateInfo_e;
// 充电状态
typedef enum
{
Uncharged = 0,
Charging,
ChargeComplete
}ChargingStateInfo_e;
typedef enum
{
Bit_RESET = 0,
Bit_SET
}BitAction;
typedef struct{
nrf_drv_gpiote_pin_t KeyPinNumber;
bool shineng;
}KeyStateInfo_t;
/* Public constants ----------------------------------------------*/
/* Public variables ----------------------------------------------*/
extern DeviceStateInfo_e DeviceState;
extern ChargingStateInfo_e ChargeState;
extern ChargingStateInfo_e ChargeLastState;
/* Public function prototypes ------------------------------------*/
void StartAdv(void);
void StopAdv(void);
void GpioInit(void);
void EXIT_KEY_Init(void);
void open_acquisition_relay(void);
void close_acquisition_relay(void);
void LedControl(void);
void StimStateInfoStructInit(SchemeData_t SchemeDataIn);
void VariableInit(void);
void StimOutCtlOpen(void);
void StimOutCtlClose(void);
void StimReleaseOpen(void);
void StimReleaseClose(void);
void PreStorageSchemeDataInit(void);
void KeyPinHandler(void);
void close_stimulate_relay(void);
void open_stimulate_relay(void);
#endif
/*************************** END OF FILE ***************************/

45
app/Inc/drv_saadc.h Normal file
View File

@@ -0,0 +1,45 @@
/********************************************************************
Copyright (c) 2021 Xiangyu Medical Co.Ltd. All rights reserved.
FileName : drv_saadc.h
Author : zhangdawei
Version : V1.0
Date :
Note :
History :
********************************************************************/
/* Includes ------------------------------------------------------*/
#ifndef DRV_SAADC_H__
#define DRV_SAADC_H__
#include "nrf_drv_timer.h"
#include "nrf_drv_saadc.h"
#include "IoControl.h"
/* Public define -------------------------------------------------*/
#define SAADC_BATTERY_CHANNEL 1 // 电池电压检测通道
#define SAADC_ELECTRODE_CHANNEL 2 // 电极片脱落电压
#define SAADC_RMS_SAMPLE_CHANNEL 0 // 肌电采样通道
#define AD_RAW_MAX 50
/* Public typedef ------------------------------------------------*/
typedef struct
{
//float AdRaw[AD_RAW_MAX+50];//原始值数组
float EmgValue;//计算值
unsigned char emgCnt;
}emg_data_t;
/* Public constants ----------------------------------------------*/
/* Public variables ----------------------------------------------*/
extern uint8_t Battery_Percentage;
/* Public function prototypes ------------------------------------*/
void battery_adc_init(void);
void timer1_output_ctrl_init(void);
void rms_saadc_init(void);
void PPIEegAdcInit(void);
void timer3_rms_init(void);
void timer3_rms_stop(void);
void timer3_rms_start(void);
void CalculateBatteryPower(void);
#endif
/*************************** END OF FILE ***************************/

241
app/Inc/drv_uart.h Normal file
View File

@@ -0,0 +1,241 @@
/********************************************************************
Copyright (c) 2021 Xiangyu Medical Co.Ltd. All rights reserved.
FileName : drv_uart.h
Author : zhangdawei
Version : V1.0
Date :
Note :
History :
********************************************************************/
/* Includes ------------------------------------------------------*/
#ifndef DRV_UART_H__
#define DRV_UART_H__
#include "sdk_config.h"
#include "ble_nus.h"
#include "ble_link_ctx_manager.h"
/* Public define -------------------------------------------------*/
#define ONE_SECOND ( 1000000 ) /* 1s = 1000000us */
/* 功能码定义 */
#define RUN_ROLL ( 0x81 ) //运行轮询
#define VERSION_INQUIRY ( 0x82 ) //版本查询
#define STIM_PARA_CONTROL ( 0x83 ) //模式、脉宽、频率、斜坡时间设置
#define START_STOP_CONTROL ( 0x84 ) //控制
#define PRE_ADJUST ( 0x85 ) //预调节
#define CURRENT_CONTROL ( 0x86 ) //电流设置
#define EEG_DATA ( 0x87 ) //肌电数据上报
#define TRIG_COLLECT_START_CONTROL ( 0x88 ) //触发采集开始
#define TRIG_STIM_START_CONTROL ( 0x89 ) //触发刺激开始
#define STATUS_INQUIRY ( 0x8A ) //轮询
#define MAC_QUERY ( 0x8B ) //MAC地址查询
#define STATE_UPLOAD ( 0x92 ) //状态上传(电极片状态和适配器状态)
#define SCHEME_QUERY ( 0x91 ) //方案查询
/* 刺激启停控制数据偏移定义 */
#define StartStopOffset ( 4 )
/* 刺激启停控制数据描述 */
#define CH_STOP ( 0x00 )
#define CH_START ( 0x01 )
#define CH_PAUSE ( 0x02 )
#define CH_CONTINUE ( 0x03 )
/* 刺激参数数据偏移定义 */
#define HeadOffset ( 0 )
#define DataLengthOffset ( 1 )
#define FunctionCodeOffset ( 2 )
#define ChannelNumOffset ( 3 )
#define SchemeCategoryOffset ( 4 )
#define SchemeIDMSBOffset ( 5 )
#define SchemeIDLSBOffset ( 6 )
#define FreqMSBOffset ( 7 )
#define FreqLSBOffset ( 8 )
#define WidthMSBOffset ( 9 )
#define WidthLSBOffset ( 10 )
#define RampUpTimeLSBOffset ( 12 )
#define RampUpTimeMSBOffset ( 11 )
#define RampSmoothTimeLSBOffset ( 14 )
#define RampSmoothTimeMSBOffset ( 13 )
#define RampDownTimeLSBOffset ( 16 )
#define RampDownTimeMSBOffset ( 15 )
#define RampBreakTimeLSBOffset ( 18 )
#define RampBreakTimeMSBOffset ( 17 )
/* 状态轮询数据偏移定义 */
#define ElectrodeStatusOffset ( 4 )
#define StimStatusOffset ( 5 )
#define StimCurrentOffset ( 6 )
#define ResetOffset ( 7 )
#define DataCrcOffset ( 8 )
#define TailOffset ( 9 )
/* 运行轮询数据偏移定义 */
#define AdapterStatusOffset ( 5 )
#define BatteryLevelOffset ( 4 )
/* 刺激电流数据偏移定义 */
#define PRE_ADJUST_OFFSET ( 4 )
/* 预调节数据定义 */
#define START_PRE_ADJUST ( 0x01 )
#define STOP_PRE_ADJUST ( 0x00 )
/* 方案存储属性定义 */
#define TEMP_STORAGE ( 1 ) // 临时存储
#define PERMANENT_STORAGE ( 2 ) // 永久存储
#define FDS_DATA_LENGTH ( 20 ) // 存储空间长度
//头尾帧
#define FRAME_HEADER (0xAA)
#define FRAME_TAIL (0x55)
//功能码
// Function Codes (Converted to UPPER_SNAKE_CASE)
#define D_PARAMETER_SET (0x71) // Original: D_ParameterSet
#define D_CHANNEL_CTRL (0x72) // Original: D_ChannelCtrl
#define D_CURRENT_SET (0x73) // Original: D_CurrentSet
#define D_EMG_DATA_REPORT (0x74) // Original: D_EmgDataReport
#define D_DATA_MODE_SWITCH (0x75) // Original: D_DataModeSwitch
#define D_PREINSTALL_CTRL (0x76) // Original: D_PreinstallCtrl
#define D_TRIGGER_MODE_CTRL (0x77) // Original: D_TriggerModeCtrl
#define D_POLL_CMD (0x78) // Original: D_PollCmd
#define D_SLICE_FALL_DETECT_SWITCH (0x79) // Original: D_SliceFallDetectSwitch
#define D_TURN_OFF_CMD (0x7A) // Original: D_TurnOffCmd
#define D_BLE_SCAN_SWITCH (0x7B) // Original: D_BleScanSwitch
#define D_MAC_ADDR_REPORT (0x7C) // Original: D_MacAddrReport
#define D_BLE_CONNECT_CMD (0x7D) // Original: D_BleConnectCmd
/* Public typedef ------------------------------------------------*/
/* 方案信息结构体 */
typedef struct
{
uint8_t StorageMode; //存储模式
uint8_t SchemeIDMSB; //方案ID
uint8_t SchemeIDLSB;
uint8_t FreqMSB; //频率
uint8_t FreqLSB;
uint8_t WidthMSB; //脉宽
uint8_t WidthLSB;
uint8_t RampUpTimeMSB; //波升
uint8_t RampUpTimeLSB;
uint8_t RampSmoothTimeMSB;//保持
uint8_t RampSmoothTimeLSB;
uint8_t RampDownTimeMSB; //波降
uint8_t RampDownTimeLSB;
uint8_t RampBreakTimeMSB; //休息
uint8_t RampBreakTimeLSB;
}SchemeData_t;
#define RMS_USER_DATA_LENGTH 4//110///4 //用户数据长度
#pragma pack(1)
typedef struct
{
uint8_t frameHeader; //帧头
uint8_t frameLength; //帧长度
uint8_t functionCode; //功能码
uint8_t myNumber; //编号
uint8_t channel; //通道号
uint16_t rmsDataBuffer[RMS_USER_DATA_LENGTH]; //数据内容
uint8_t checkSum; //校验和
uint8_t frameTail; //帧尾
} rms_data_t;
typedef struct
{
uint8_t frameHeader; //帧头
uint8_t frameLength; //帧长度
uint8_t functionCode; //功能码
uint8_t channel; //通道号
uint8_t ChargeState; //充电状态
uint8_t BatteryLevelA; //A 电池电量
uint8_t BatteryLevelB; //B 电池电量
uint8_t BatteryLevelC; //C 电池电量
uint8_t reserve[5]; //保留字段
uint8_t checkSum; //校验和
uint8_t frameTail; //帧尾
} reply_run_status_t;
#define MAX_VERSION_LEN 15
typedef struct
{
uint8_t frameHeader; //帧头
uint8_t frameLength; //帧长度
uint8_t functionCode; //功能码
uint8_t channel; //通道号
uint8_t VersionDes[MAX_VERSION_LEN]; //版本描述
uint8_t checkSum; //校验和
uint8_t frameTail; //帧尾
} check_version_t;
#pragma pack()
//设备连接状态
typedef enum
{
DisconnectState = 0,
ConnectState
}ConnectStateInfo_e;
// 电极片状态
typedef enum
{
ElectrodeFalloff,
ElectrodeConnectted
}ElectrodeStatusInfo_e;
// 适配器连接状态
typedef enum
{
AdapterNotConnected = 0,
AdapterConnected
}AdapterStateInfo_e;
//FDS异步操作标志结构体
typedef struct
{
bool scheme_update; //scheme_record记录更新标志
bool read; //读记录标志
bool gc; //碎片收集标志
bool busy; //FDS忙标志
}my_fds_info_t;
//记录scheme的id和内容
typedef struct
{
uint8_t text[FDS_DATA_LENGTH];
}__attribute__((aligned(4)))SchemePara_t;
/* Public constants ----------------------------------------------*/
/* Public variables ----------------------------------------------*/
extern uint16_t m_conn_handle;
extern uint16_t m_ble_nus_max_data_len;
extern uint8_t CurrentFlag;
extern ConnectStateInfo_e DeviceConnectState;
extern ElectrodeStatusInfo_e ElectrodeStatusInfo;
extern my_fds_info_t my_fds_info;
extern SchemePara_t SchemePara;
extern SchemeData_t SchemeData;
extern uint8_t BLE_MAC[BLE_GAP_ADDR_LEN];
extern AdapterStateInfo_e AdapterState;
extern AdapterStateInfo_e LastAdapterState;
extern SchemeData_t PreStorageSchemeData;
extern ElectrodeStatusInfo_e LastElectrodeStatusInfo;
extern uint16_t ccrvaluebuf[70];
extern uint8_t eegflag;
/* Public function prototypes ------------------------------------*/
void service_nus_init(void);
void StartManage(void);
void PauseManage(void);
void RecoverManage(void);
void StopManage(void);
void CloseOutput(void);
void SetStandardCurrent(uint8_t* AnalysisDataBfferIn_t);
void RunRoll(void);
void CheckVersion(void);
void UpdateCurrent(uint8_t CurrentSend);
void JudgeLedMode(void);
void DisconnectControl(void);
void SchemeQuery(uint8_t idMSB,uint8_t idLSB);
void UpdateControlStatus(uint8_t ControlStatus);
void StateUpLoad(AdapterStateInfo_e AdapterStateTemp , ElectrodeStatusInfo_e ElectrodeStatusTemp);
void EegDataSend(void);
void user_ble_or_uart_send(char * txBufferP, uint16_t Length);
void ble_send_rms_data(uint16_t rms_data);
#endif
/*************************** END OF FILE ***************************/

117
app/Inc/timer.h Normal file
View File

@@ -0,0 +1,117 @@
/********************************************************************
Copyright (c) 2021 Xiangyu Medical Co.Ltd. All rights reserved.
FileName : timer.h
Author : zhangdawei
Version : V1.0
Date :
Note :
History :
********************************************************************/
#ifndef TIMER_H__
#define TIMER_H__
/* Includes ------------------------------------------------------*/
#include "app_timer.h"
/* Public define -------------------------------------------------*/
#define RAMP_TIM_TIME ( 1000 )
/* 呼吸灯的步长 */
#define M_STEP ( 100 )
/* 频率对应的周期值 */
#define CYCLE_CALUE ( 1000 )
#define SIQU ( 200 )
/* Public typedef ------------------------------------------------*/
/*通道状态*/
typedef enum
{
IdleState = 0, // 空闲态
StimState, // 刺激态
StimAdjState // 刺激调节态
}ChannelState_e;
typedef enum
{
E_RMS_STOP = 0x00,
E_RMS_START = 0x01,
E_RMS_PAUSE = 0x02,
E_RMS_CONTINUE = 0x03,
E_STIM_STOP = 0x04,
E_STIM_START = 0x05,
E_STIM_PAUSE = 0x06,
E_STIM_CONTINUE = 0x07
}E_WORK_STATE;
/* LED状态 */
typedef enum
{
Disconnect_Highbattery = 0, //未连接有电
Disconnect_Lowbattery, // 未连接低电
Connect_Lowbattery, // 连接低电
Connect_Highbattery, // 连接有电
Null
}LedModeInfo_e;
/* 斜坡状态 */
typedef enum
{
RampIdle = 0,
RampUp,
RampDown,
RampSmooth,
RampBreak
}RampState_e;
/* 通道工作状态 */
typedef enum
{
Close,
Open,
Pause,
Continue,
Ready
}ChannelWorkState_e;
/* 刺激参数信息结构体 */
typedef struct
{
uint16_t usRampUpTime; // 上坡时间 单位为ms
uint16_t usRampDownTime; // 下坡时间
uint16_t usSmoothTime; // 平坡时间
uint16_t usBreakTime; // 休息时间
uint16_t usFreqBuf; // 频率
ChannelWorkState_e ChannelWorkState; // 通道当前工作状态
uint32_t usWidth; // 脉冲宽度
uint32_t usCycle; // 脉冲周期
RampState_e Ramp_e; // 斜坡状态
uint8_t ucCurrent; // 标称电流值
uint16_t usCcrValue; // 标称的CCR值
}StimStateInfo_t;
/* Public constants ----------------------------------------------*/
/* Public variables ----------------------------------------------*/
extern StimStateInfo_t ChannelStimStateInfo_t;
extern uint8_t PwmLedInit_Flag;
extern bool LongPressEvent;
extern bool BeepBeepOpenFlag;
/* Public function prototypes ------------------------------------*/
void AppTimersInit(void);
void ApplicationTimersStart(void);
void PwmDACInit(void);
void PwmDACStop(void);
void PwmLedUnInit(void);
void PwmSubtractStop(void);
void GpioteInit(void);
void PPIPwmInit(void);
void SetCurrent(uint16_t CcrIn);
void OutputCurrentCtrl(void);
void BlinkTwice(void);
void pwm0_common_init(void);
void SetPWMValues(uint16_t tempvalue);
void pwm2common_init(void);
void PwmDACPlay(void);
void pwm2_play(void);
void pwm0_play(void);
#endif
/*************************** END OF FILE ***************************/

31
app/Inc/user_config.h Normal file
View File

@@ -0,0 +1,31 @@
/********************************************************************
Copyright (c) 2021 Xiangyu Medical Co.Ltd. All rights reserved.
FileName :
Author : xiaozhengsheng
Version : V1.0
Date :
Note :
History :
********************************************************************/
#ifndef _USER_CONFIG_H_
#define _USER_CONFIG_H_
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#define SOFT_VERSION_MAX_LENGTH 50
typedef struct {
uint8_t major; // 主版本号 (V1)
uint8_t minor; // 次版本号 (.0)
uint8_t patch; // 修订号 (.0)
uint8_t build; // 构建号 (.0)
uint8_t testVersion[30];
} version_t;
extern char softWareVersion[SOFT_VERSION_MAX_LENGTH];
void read_config_user_config(void);
#endif
/*************************** END OF FILE ***************************/

201
app/Src/IoControl.c Normal file
View File

@@ -0,0 +1,201 @@
/********************************************************************
Copyright (c) 2021 Xiangyu Medical Co.Ltd. All rights reserved.
FileName : IoControl.c
Author : zhangdawei
Version : V1.0
Date :
Note :
History :
********************************************************************/
/* Includes ------------------------------------------------------*/
#include "IoControl.h"
#include "drv_uart.h"
#include "nrf_drv_gpiote.h"
/* Private define ------------------------------------------------*/
/* Private typedef -----------------------------------------------*/
/* Private constants ---------------------------------------------*/
/* Private variables ---------------------------------------------*/
uint8_t LastState = Null; //保存上一次的状态
/* Private function prototypes -----------------------------------*/
/* Public constants ----------------------------------------------*/
/* Public variables ----------------------------------------------*/
DeviceStateInfo_e DeviceState = POWER_CLOSE; //设备开关机状态
ChargingStateInfo_e ChargeState = Uncharged;
ChargingStateInfo_e ChargeLastState = Uncharged;
KeyStateInfo_t KeyStateInfo;
/********************************************************************
* name : void GpioInit(void)
* description : GPIO引脚初始化
* Input : void
* Output : void
* Return :
********************************************************************/
void GpioInit(void)
{
NRF_UICR->NFCPINS = 0;
nrf_gpio_cfg_output(KEY_POWER);
nrf_gpio_cfg_output(LED_YELLOW);
// nrf_gpio_cfg_output(CHARGE_LED);
nrf_gpio_cfg_output(STIM_RMS_RELAY_PIN);
nrf_gpio_cfg_output(STIM_RELAY_PIN);
nrf_gpio_cfg_output(SAMPLE_POWER_PIN);
// nrf_gpio_pin_clear(KEY_POWER);
nrf_gpio_pin_clear(LED_YELLOW);
// nrf_gpio_pin_clear(CHARGE_LED);
nrf_gpio_pin_clear(STIM_RMS_RELAY_PIN);
nrf_gpio_pin_clear(STIM_RELAY_PIN);
nrf_gpio_pin_clear(SAMPLE_POWER_PIN);
/////////
nrf_gpio_cfg_input(CHG_MANAGER_CHG, NRF_GPIO_PIN_NOPULL);//检测充电状态
}
/* 电刺激输出使能脚 */
void StimOutCtlOpen(void)
{
}
void StimOutCtlClose(void)
{
}
/* 能量释放控制 */
void StimReleaseOpen(void)
{
}
void StimReleaseClose(void)
{
}
// OK按键处理函数
void KEY_PWR_SWITICH_Handler(void)
{
}
//GPIOTE事件处理函回调函数事件回调函数里面可以获取pin编号和引脚状态变化
void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
if(pin == KEY_PWR_SWITICH)
{
KeyStateInfo.shineng = true;
KeyStateInfo.KeyPinNumber = KEY_PWR_SWITICH;
}
}
void EXIT_KEY_Init(void)
{
ret_code_t err_code;
nrf_gpio_cfg_input(KEY_PWR_SWITICH, NRF_GPIO_PIN_PULLUP);//检测开关机信号
}
/********************************************************************
* name : void open_acquisition_relay(void)
* description : 打开采样继电器
* Input : void
* Output : void
* Return :
********************************************************************/
void open_acquisition_relay(void)
{
nrf_gpio_pin_set(STIM_RMS_RELAY_PIN); //闭合继电器
nrf_gpio_pin_clear(STIM_RELAY_PIN); //打开刺激继电器
}
/********************************************************************
* name : void close_acquisition_relay(void)
* description : 关闭采样继电器
* Input : void
* Output : void
* Return :
********************************************************************/
void close_acquisition_relay(void)
{
nrf_gpio_pin_clear(STIM_RMS_RELAY_PIN);
nrf_gpio_pin_clear(STIM_RELAY_PIN);
}
/********************************************************************
* name : void open_stimulate_relay(void)
* description : 打开刺激继电器
* Input : void
* Output : void
* Return :
********************************************************************/
void open_stimulate_relay(void)
{
nrf_gpio_pin_clear(STIM_RMS_RELAY_PIN);
nrf_gpio_pin_set(STIM_RELAY_PIN);
}
/********************************************************************
* name : void close_stimulate_relay(void)
* description : 关闭刺激继电器
* Input : void
* Output : void
* Return :
********************************************************************/
void close_stimulate_relay(void)
{
nrf_gpio_pin_clear(STIM_RMS_RELAY_PIN);
nrf_gpio_pin_clear(STIM_RELAY_PIN);
}
/********************************************************************
* name : void StimStateInfoStructInit(SchemeData_t SchemeDataIn)
* description : 初始化通道信息描述结构体
* Input : void
* Output : void
* Return :
********************************************************************/
void StimStateInfoStructInit(SchemeData_t SchemeDataIn)
{
}
// 设备预存信息初始化ID :101 腹直肌分离
void PreStorageSchemeDataInit(void)
{
}
void KeyPinHandler(void)
{
if(KeyStateInfo.shineng == true)
{
switch(KeyStateInfo.KeyPinNumber)
{
case KEY_PWR_SWITICH:
{
KEY_PWR_SWITICH_Handler();
break;
}
}
KeyStateInfo.shineng = false;
}
}
/********************************************************************
* name : void VariableInit(void)
* description : 初始化变量
* Input : void
* Output : void
* Return :
********************************************************************/
void VariableInit(void)
{
}
/*************************** END OF FILE ***************************/

364
app/Src/drv_saadc.c Normal file
View File

@@ -0,0 +1,364 @@
/********************************************************************
Copyright (c) 2021 Xiangyu Medical Co.Ltd. All rights reserved.
FileName : drv_saadc.c
Author : zhangdawei
Version : V1.0
Date :
Note :
History :
********************************************************************/
/* Includes ------------------------------------------------------*/
#include "nrf_drv_ppi.h"
#include "app_timer.h"
#include "bsp_btn_ble.h"
#include "drv_saadc.h"
#include "timer.h"
#include "string.h"
#include "drv_uart.h"
#include "nrf_drv_ppi.h"
#include "IIR.h"
/* Private define ------------------------------------------------*/
/* Private typedef -----------------------------------------------*/
#define SAMPLES_BUFFER_LEN 10
/* Private constants ---------------------------------------------*/
//定义Timer1的驱动程序实例。驱动程序实例的ID对应Timer的ID如NRF_DRV_TIMER_INSTANCE(1)对应Timer1
const nrfx_timer_t TIMER_ADC = NRF_DRV_TIMER_INSTANCE(4);
static nrf_ppi_channel_t m_ppi_channel5;
const nrfx_timer_t TIMER3_RMS = NRFX_TIMER_INSTANCE(3); //读取rms值的定时器3
static nrf_saadc_value_t m_buffer_pool[2][SAMPLES_BUFFER_LEN];
/* Private variables ---------------------------------------------*/
/* Private function prototypes -----------------------------------*/
/* Public constants ----------------------------------------------*/
/* Public variables ----------------------------------------------*/
//电池电压回调函数,堵塞模式,不需要事件,定义空的事件回调函数
void saadc_battery_callback(nrfx_saadc_evt_t const * p_event){}
//刺激反馈电压,堵塞模式,不需要事件,定义空的事件回调函数
void saadc_stimulate_callback(nrfx_saadc_evt_t const * p_event){}
/********************************************************************
* name : void battery_adc_init(void)
* description : ADC初始化,刺激电压检测、电池电压检测、电极脱落电压检测
* Input : void
* Output : void
* Return :
********************************************************************/
void battery_adc_init(void)
{
ret_code_t err_code;
//初始化SAADC注册事件回调函数。空函数即可
err_code = nrf_drv_saadc_init(NULL, saadc_battery_callback);
APP_ERROR_CHECK(err_code);
//配置通道1输入引脚为AIN7电池电压
nrf_saadc_channel_config_t channeltwo_config =
NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN7);
err_code = nrfx_saadc_channel_init(SAADC_BATTERY_CHANNEL, &channeltwo_config);
APP_ERROR_CHECK(err_code);
// //配置通道2输入引脚为AIN4电极片脱落电压检测
// nrf_saadc_channel_config_t channelthree_config =
// NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN4);
// //初始化SAADC通道3
// err_code = nrfx_saadc_channel_init(SAADC_ELECTRODE_CHANNEL, &channelthree_config);
// APP_ERROR_CHECK(err_code);
}
// 如需更高精度非线性计算(示例)
float nonlinear_percentage(float voltage) {
// 三元锂电池的典型放电曲线拟合多项式
float p = (voltage - 3.4f) / 0.8f; // 归一化到[0,1]
return 100 * (1.0f - 0.2f*p - 0.8f*p*p);
}
// 锂电池电量计算函数
// 输入:当前电压(单位:伏特)
// 输出电量百分比0~100
int calculate_battery_percentage(float voltage)
{
// 定义电压范围
const float VOLTAGE_MIN = 3.4f; // 0%电量对应电压
const float VOLTAGE_MAX = 4.195f; // 100%电量对应电压
// 边界检查
if (voltage <= VOLTAGE_MIN) {
return 0;
} else if (voltage >= VOLTAGE_MAX) {
return 100;
}
// 线性插值计算百分比(可根据实际放电曲线修改算法)
float percentage = (voltage - VOLTAGE_MIN) / (VOLTAGE_MAX - VOLTAGE_MIN) * 100.0f;
// 四舍五入取整
return (int)(percentage + 0.5f);
}
/********************************************************************
* name : void CalculateBatteryPower(void)
* description : 计算电池电量0-100
* Input : void
* Output : void
* Return : void
********************************************************************/
void CalculateBatteryPower(void)
{
uint16_t Adctemp;
nrf_saadc_value_t Battery_Saadc_Value;
//启动一次ADC采样,存在Battery_Saadc_Value中
nrfx_saadc_sample_convert(SAADC_BATTERY_CHANNEL, &Battery_Saadc_Value);
Adctemp = ((Battery_Saadc_Value * 3600) / 16384);
static float lastBatteryPercentage = 100;
int temp = calculate_battery_percentage(Adctemp * 5.12557);
if(Uncharged == ChargeState) //如果没有处于充电状态,不允许电量上升,只能下降
{
if(temp < lastBatteryPercentage)
Battery_Percentage = temp;
}
else
{
Battery_Percentage = temp;
}
lastBatteryPercentage = Battery_Percentage;
}
short short_to_big_endian(short value)
{
uint8_t buf[2];
short resultShort;
buf[0] = (value >> 8) & 0xFF; // 高字节Big-Endian 第一个字节)
buf[1] = value & 0xFF; // 低字节Big-Endian 第二个字节)
memcpy(&resultShort,buf,2);
return resultShort;
}
/********************************************************************
* name : void rms_saadc_callback(nrfx_saadc_evt_t const * p_event)
* description : 肌电采集SAADC事件回调函数只有一个缓存填满后才会进入事件回调函数
* Input : nrfx_saadc_evt_t const * p_eventsaadc事件
* Output : void
* Return :
********************************************************************/
void rms_saadc_callback(nrfx_saadc_evt_t const * p_event)
{
static float32_t AdcFilter_g[AD_RAW_MAX] __attribute__((aligned(4)));
float32_t outFilter __attribute__((aligned(4)));
float32_t vol __attribute__((aligned(4)));
static emg_data_t emgData=
{
.emgCnt = 0
};
if(p_event->type == NRF_DRV_SAADC_EVT_DONE)
{
ret_code_t err_code;
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_BUFFER_LEN);
APP_ERROR_CHECK(err_code);
}
for(int i = 0; i < SAMPLES_BUFFER_LEN; i++)
{
if(p_event->data.done.p_buffer[i] < 0)
vol = 0;
else
{
vol = (p_event->data.done.p_buffer[i]*3600/16384) - 1545; //
}
bs_bp(&vol, &outFilter); //5.6us
if(emgData.emgCnt < AD_RAW_MAX) //防止溢出
AdcFilter_g[emgData.emgCnt] = outFilter;
emgData.emgCnt++;
if(emgData.emgCnt >= AD_RAW_MAX)
{
emgData.emgCnt=0;
float32_t temp __attribute__((aligned(4)));
arm_rms_f32(AdcFilter_g, AD_RAW_MAX, &temp);//6us
emgData.EmgValue = temp*0.8806 - 3.028;
if(emgData.EmgValue < 0)
emgData.EmgValue = 0;
// if(temp < 0)
// emgData.EmgValue = 0;
// else
// emgData.EmgValue = 0.6722 * temp + 3.7097;
uint16_t outfRms;
outfRms = (short)emgData.EmgValue;
outfRms = short_to_big_endian(outfRms);
ble_send_rms_data(outfRms);
}
//ble_send_rms_data(save_value[i]);
}
}
void timer3_rms_handler(nrf_timer_event_t event_type, void* p_context)
{
}
/********************************************************************
* name : void timer3_rms_init(void)
* description : 定时器1初始化
* Input : void
* Output : void
* Return :
********************************************************************/
void timer3_rms_init(void)
{
uint32_t err_code = NRF_SUCCESS;
uint32_t time_us = 500; //采样频率2kHz
uint32_t time_ticks;
nrfx_timer_config_t timer3_cfg = NRFX_TIMER_DEFAULT_CONFIG;
err_code = nrfx_timer_init(&TIMER3_RMS, &timer3_cfg, timer3_rms_handler);
APP_ERROR_CHECK(err_code);
time_ticks = nrfx_timer_us_to_ticks(&TIMER3_RMS, time_us);
nrfx_timer_extended_compare(
&TIMER3_RMS, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
}
/********************************************************************
* name : void timer3_rms_start(void)
* description : 开启定时器1
* Input : void
* Output : void
* Return :
********************************************************************/
void timer3_rms_start(void)
{
nrfx_timer_enable(&TIMER3_RMS);
}
/********************************************************************
* name : void timer3_rms_stop(void)
* description : 停止定时器1
* Input : void
* Output : void
* Return :
********************************************************************/
void timer3_rms_stop(void)
{
nrfx_timer_disable(&TIMER3_RMS);
}
/********************************************************************
* name : void PPIEegAdcInit(void)
* description : ADCPPI通道初始化
* Input : void
* Output : void
* Return :
********************************************************************/
void PPIEegAdcInit(void)
{
uint32_t err_code = NRF_SUCCESS;
err_code = nrf_drv_ppi_init();
APP_ERROR_CHECK(err_code);
//分配PPI通道PPI通道的分配是由驱动函数完成的分配的通道号保存到my_ppi_channel1中
err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel5);
APP_ERROR_CHECK(err_code);
//分配PPI通道的EEP和TEP
err_code = nrf_drv_ppi_channel_assign(m_ppi_channel5,
nrf_drv_timer_event_address_get(&TIMER3_RMS, NRF_TIMER_EVENT_COMPARE0),
nrf_drv_saadc_sample_task_get());
APP_ERROR_CHECK(err_code);
//使能PPI通道
err_code = nrf_drv_ppi_channel_enable(m_ppi_channel5);
APP_ERROR_CHECK(err_code);
}
/********************************************************************
* name : void rms_saadc_init(void)
* description : 肌电采集saadc初始化
* Input : void
* Output : void
* Return :
********************************************************************/
void rms_saadc_init(void)
{
ret_code_t err_code;
nrf_drv_saadc_uninit(); //先反初始化
//配置通道0输入引脚为AIN3采集信号
nrf_saadc_channel_config_t channel_config =
NRFX_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
//初始化SAADC注册事件回调函数。
err_code = nrf_drv_saadc_init(NULL, rms_saadc_callback);
APP_ERROR_CHECK(err_code);
//初始化SAADC通道0
err_code = nrfx_saadc_channel_init(SAADC_RMS_SAMPLE_CHANNEL, &channel_config);
APP_ERROR_CHECK(err_code);
//配置缓存1将缓存1地址赋值给SAADC驱动程序中的控制块m_cb的一级缓存指针
err_code = nrfx_saadc_buffer_convert(m_buffer_pool[0], SAMPLES_BUFFER_LEN);
APP_ERROR_CHECK(err_code);
//配置缓存2将缓存1地址赋值给SAADC驱动程序中的控制块m_cb的二级缓存指针
err_code = nrfx_saadc_buffer_convert(m_buffer_pool[1], SAMPLES_BUFFER_LEN);
APP_ERROR_CHECK(err_code);
}
// 电极脱落检测
void ElectFallOff(void)
{
}
/********************************************************************
* name : void timer4_output_ctrl_handler(nrf_timer_event_t event_type, void* p_context)
* description : 变压器电压读取、变压器开关控制、脱落检测、刺激自调节
* Input : nrf_timer_event_t event_type定时器事件类型
void* p_context
* Output : void
* Return :
********************************************************************/
void timer1_output_ctrl_handler(nrf_timer_event_t event_type, void* p_context)
{
switch(event_type)
{
case NRF_TIMER_EVENT_COMPARE0:
//ElectFallOff();
break;
default:
break;
}
}
/********************************************************************
* name : void timer1_output_ctrl_init(void)
* description : 输出控制定时器初始化
* Input : void
* Output : void
* Return :
********************************************************************/
void timer1_output_ctrl_init(void)
{
ret_code_t err_code = NRF_SUCCESS;
uint32_t time_us = 50;
uint32_t time_ticks;
nrfx_timer_config_t timer_cfg = NRFX_TIMER_DEFAULT_CONFIG;
err_code = nrfx_timer_init(&TIMER_ADC, &timer_cfg, timer1_output_ctrl_handler);
APP_ERROR_CHECK(err_code);
//定时时间(单位us)转换为ticks
time_ticks = nrfx_timer_us_to_ticks(&TIMER_ADC, time_us);
//设置定时器捕获/比较通道及该通道的比较值,使能通道的比较中断
nrfx_timer_extended_compare(
&TIMER_ADC, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
nrfx_timer_enable(&TIMER_ADC);
}
/*************************** END OF FILE ***************************/

817
app/Src/drv_uart.c Normal file
View File

@@ -0,0 +1,817 @@
/********************************************************************
Copyright (c) 2021 Xiangyu Medical Co.Ltd. All rights reserved.
FileName : drv_uart.c
Author : zhangdawei
Version : V1.0
Date :
Note :
History :
********************************************************************/
/* Includes ------------------------------------------------------*/
#include "drv_uart.h"
#include "pca10040.h"
#include "ble_nus.h"
#include "time.h"
#include "IoControl.h"
#include "nrf_drv_timer.h"
#include "fds.h"
/* Private define ------------------------------------------------*/
BLE_NUS_DEF(m_nus); //定义名称为m_nus的串口透传服务实例
/* Private typedef -----------------------------------------------*/
/* Private constants ---------------------------------------------*/
/* 接收到的方案信息 */
SchemeData_t SchemeData;
SchemeData_t PreStorageSchemeData;// 设备预存信息
uint16_t m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3; /**< Maximum length of data (in bytes) that can be transmitted to the peer by the Nordic UART service module. */
/* Private variables ---------------------------------------------*/
//该变量用于保存连接句柄,初始值设置为无连接
uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID;
/* 电池电量的原始值 */
nrf_saadc_value_t Battery_Saadc_Value = 10000;
/* 电池电压百分比 */
uint8_t Battery_Percentage = 100;
/* 肌电采集定时器初始化标志 */
uint8_t TimerEegAdcInit_Flag = 0;
/* 控制电流标志 */
uint8_t CurrentFlag = 0;
/* 设备的连接状态 */
ConnectStateInfo_e DeviceConnectState = DisconnectState;
ElectrodeStatusInfo_e ElectrodeStatusInfo = ElectrodeConnectted;
ElectrodeStatusInfo_e LastElectrodeStatusInfo = ElectrodeConnectted;
/* 适配器连接状态 */
AdapterStateInfo_e AdapterState = AdapterNotConnected;
AdapterStateInfo_e LastAdapterState = AdapterNotConnected;
/* MAC地址数组 */
uint8_t BLE_MAC[BLE_GAP_ADDR_LEN];
//定义FDS异步操作标志结构体
my_fds_info_t my_fds_info;
// 初始化参数信息
SchemePara_t SchemePara = {
.text = 0
};
uint16_t ccrvaluebuf[70] = {
23,25,27,29,31,33,35,37,39,41, //1-10
43,45,47,50,52,54,56,58,60,63, //11-20
65,67,69,71,74,76,78,80,82,85, //21-30
87,89,91,93,95,97,99,101,103,105, //31-40
108,111,114,117,120,123,126,129,132,134, //41-50
136,138,140,142,144,146,148,150,152,154, //51-60
155,156,158,160,162,164,167,170,172,174 //61-70
};
#define EEG_DATA_LENGTH ( 50 )
uint8_t aEEGData[EEG_DATA_LENGTH] = {
23,25,27,29,31,33,35,37,39,41,
43,45,47,50,52,54,56,58,60,63,
65,67,69,71,74,76,78,80,82,85,
87,89,91,93,95,97,99,101,103,105,
108,111,114,117,120,123,126,129,132,134
};
uint8_t eegmode = 0;
uint8_t eegflag = 0;
StimStateInfo_t ChannelStimStateInfo_t;
rms_data_t rmsData = {.frameHeader = FRAME_HEADER,
.frameLength = sizeof(rmsData) - 4, //减去帧头、帧长度、校验和、帧尾
.functionCode = D_EMG_DATA_REPORT,
.myNumber = 0x01,
.channel = 0x01,
//.rmsDataBuffer = {0},
.checkSum = 0,
.frameTail = FRAME_TAIL
};
/* Private function prototypes -----------------------------------*/
void StatusInquiry(ElectrodeStatusInfo_e Electrodestatus);
void StartStopCtrl(uint8_t StartStopValue);
void MACQuery(void);
/* Public constants ----------------------------------------------*/
/* Public variables ----------------------------------------------*/
/********************************************************************
* name : void SetFreqWidth(uint8_t* AnalysisDataBffer_t)
* description : 设置脉冲频率
* Input : AnalysisDataBffer_t接收到的数据缓冲区
* Output : void
* Return : void
********************************************************************/
void SetFreqWidth(uint8_t* AnalysisDataBffer_t)
{
}
/********************************************************************
* name : void SetRampTime(uint8_t* AnalysisDataBfferIn_t)
* description : 设置斜坡时间
* Input : AnalysisDataBffer_t接收到的数据缓冲区
* Output : void
* Return : void
********************************************************************/
void SetRampTime(uint8_t* AnalysisDataBffer_t)
{
}
/********************************************************************
* name : void SetStimPara(uint8_t* AnalysisDataBfferIn_t)
* description : 设置刺激参数
* Input : AnalysisDataBffer_t接收到的数据缓冲区
* Output : void
* Return : void
********************************************************************/
void SetStimPara(uint8_t* AnalysisDataBffer_t)
{
//开启采集模式
if(1 == AnalysisDataBffer_t[4])
{
eegmode = 1;
return;
}
}
/********************************************************************
* name : static void nus_data_handler(ble_nus_evt_t * p_evt)
* description : BLE串口透传事件回调函数
* Input : ble_nus_evt_t * p_evt发生的事件
* Output : void
* Return : void
********************************************************************/
static void nus_data_handler(ble_nus_evt_t * p_evt)
{
uint8_t FrameLength = 0;//帧长度
uint8_t FunctionCode = 0;//功能码
uint8_t CheckSum = 0;//校验和
uint8_t Sum = 0;//数据和
uint8_t SumCCR = 0;//CCR值
#define RX_BLE_DATA_LEN 100
uint8_t RecieveData[RX_BLE_DATA_LEN] = {0}; //接收数据帧的数组
if(p_evt->type == BLE_NUS_EVT_RX_DATA)
{
uint16_t Length = 0;
Length = p_evt->params.rx_data.length;
if(Length > RX_BLE_DATA_LEN)
{
NRF_LOG_ERROR("RecieveData length error");
return;
}
else
{
memcpy(RecieveData, p_evt->params.rx_data.p_data, Length);
}
/* 解析蓝牙收到的数据 */
if(0xAA == RecieveData[0])
{
FrameLength = RecieveData[1]; //帧长度
if(FrameLength > (RX_BLE_DATA_LEN - 4))
{
NRF_LOG_ERROR("RecieveData length error %d", FrameLength);
return;
}
else if(0x55 != RecieveData[FrameLength + 3]) //查找帧尾
{
NRF_LOG_ERROR("RecieveData end flag error");
return;
}
CheckSum = RecieveData[FrameLength + 2]; //校验和
//if(0x55 == RecieveData[FrameLength + 3]) //查找帧尾
{
/*校验数据包*/
for(uint8_t i = 0; i < FrameLength; i++)
{
Sum += RecieveData[i+2];
}
SumCCR = Sum & 0xff;
if(CheckSum == SumCCR)
{
FunctionCode = RecieveData[2];
/*模式、脉宽、频率、斜坡时间设置*/
if(STIM_PARA_CONTROL == FunctionCode)
{
SetStimPara(RecieveData); //设置参数
ble_nus_data_send(&m_nus, RecieveData, &Length, m_conn_handle);//将收到的数据回复回去
}
/*电流控制 (控制刺激强度)*/
else if(CURRENT_CONTROL == FunctionCode)
{
NRF_LOG_INFO("CURRENT_CONTROL");
// SetStandardCurrent(RecieveData); //设置电流
// CurrentFlag = 1; //设置电流的标志此标志为1时表示可以输出电流
ble_nus_data_send(&m_nus, RecieveData, &Length, m_conn_handle);//将收到的数据回复回去
}
/*刺激启停控制*/
else if(START_STOP_CONTROL == FunctionCode)
{
NRF_LOG_INFO("START_STOP_CONTROL");
// CurrentFlag = 0; //此标志清0此时开启电流的标志由通道模式和通道状态控制
uint8_t StartStopValue = CH_STOP; //初始状态为停止
StartStopValue = RecieveData[StartStopOffset]; //设置启停状态
StartStopCtrl(StartStopValue); //启停控制
ble_nus_data_send(&m_nus, RecieveData, &Length, m_conn_handle);//将收到的数据回复回去
}
/*触发采集开始*/
else if(TRIG_COLLECT_START_CONTROL == FunctionCode)
{
NRF_LOG_INFO("TRIG_COLLECT_START_CONTROL");
ble_nus_data_send(&m_nus, RecieveData, &Length, m_conn_handle);//将收到的数据回复回去
}
/*触发刺激开始*/
else if(TRIG_STIM_START_CONTROL == FunctionCode)
{
NRF_LOG_INFO("TRIG_STIM_START_CONTROL");
ble_nus_data_send(&m_nus, RecieveData, &Length, m_conn_handle);//将收到的数据回复回去
}
/*轮询0x8A*/
else if(STATUS_INQUIRY == FunctionCode)
{
StatusInquiry(ElectrodeStatusInfo);
NRF_LOG_INFO("STATUS_INQUIRY");
}
/* 运行轮询 */
else if(RUN_ROLL == FunctionCode)
{
RunRoll();
NRF_LOG_INFO("RUN_ROLL");
}
/* 版本查询 */
else if(VERSION_INQUIRY == FunctionCode)
{
CheckVersion();
NRF_LOG_INFO("VERSION_INQUIRY");
}
else if(SCHEME_QUERY == FunctionCode)
{
//SchemeQuery();
my_fds_info.read = true;
NRF_LOG_INFO("SCHEME_QUERY");
}
else if(MAC_QUERY == FunctionCode)
{
MACQuery();
}
}
}
}
/***********end***********/
}
}
/********************************************************************
* name : void service_nus_init(void)
* description : 串口透传服务初始化
* Input : void
* Output : void
* Return : void
********************************************************************/
void service_nus_init(void)
{
ret_code_t err_code;
ble_nus_init_t nus_init; //定义串口透传初始化结构体
/*------------------以下代码初始化串口透传服务-------------*/
memset(&nus_init, 0, sizeof(nus_init)); //清零串口透传服务初始化结构体
nus_init.data_handler = nus_data_handler; //设置串口透传事件回调函数
err_code = ble_nus_init(&m_nus, &nus_init); //初始化串口透传服务
APP_ERROR_CHECK(err_code);
/*------------------初始化串口透传服务-END-----------------*/
}
/********************************************************************
* name : void StartStopCtrl(uint8_t ucStartStopValueIn)
* description : 启、停控制处理函数
* Input : ucStartStopValueIn启、停控制值
ucChannelIn通道号
* Output : void
* Return : void
********************************************************************/
void StartStopCtrl(uint8_t StartStopValue)
{
switch(StartStopValue)
{
case CH_START: StartManage();
break;
case CH_PAUSE: PauseManage();
break;
case CH_CONTINUE: RecoverManage();
break;
case CH_STOP: StopManage();
break;
default: break;
}
}
/********************************************************************
* name : void StartManage(void)
* description : 开始处理函数
* Input : void
* Output : void
* Return : void
********************************************************************/
void StartManage(void)
{
if(eegmode == 1)
{
eegflag = 1;
}
}
/********************************************************************
* name : void PauseManage(void)
* description : 暂停处理函数
* Input : Channel通道号
* Output : void
* Return : void
********************************************************************/
void PauseManage(void)
{
if(eegmode == 1)
{
eegflag = 0;
}
// CHAChannelState_e = IdleState; //设置状态为空闲态空闲状态下pwm_flag置0
// ChannelStimStateInfo_t.ChannelWorkState = Pause;
// PwmDACStop();
// CloseOutput(); //关闭输出
// NRF_LOG_INFO("Pause_Stim");
}
/********************************************************************
* name : void CloseOutput(void)
* description : 将输出电流置0斜坡时间置0,关闭输出PWM
* Input : void
* Output : void
* Return : void
********************************************************************/
void CloseOutput(void)
{
if(eegmode == 1)
{
eegmode = 0;
eegflag = 0;
}
//ChannelStimStateInfo_t.ucCurrent = 0;
// ChannelStimStateInfo_t.Ramp_e = RampIdle;//斜坡状态为空闲态
// RampTime = 0; //斜坡时间清0
// SetCurrent(0); //设置输出电流为0
// PwmSubtractStop(); //关闭输出PWM
// PwmDACStop(); //停止充能PWM
//// GpioteInit();
}
/********************************************************************
* name : void RecoverManage(void)
* description : 继续处理函数
* Input : void
* Output : void
* Return : void
********************************************************************/
void RecoverManage(void)
{
if(eegmode == 1)
{
eegflag = 1;
}
}
/********************************************************************
* name : void StopManage(void)
* description : 停止处理函数
* Input : void
* Output : void
* Return : void
********************************************************************/
void StopManage(void)
{
}
/********************************************************************
* name : void SetStandardCurrent(uint8_t* AnalysisDataBfferIn_t)
* description : 设置标称电流值
* Input : AnalysisDataBfferIn_t接收到的数据缓冲区
* Output : void
* Return : void
********************************************************************/
void SetStandardCurrent(uint8_t* AnalysisDataBffer_t)
{
}
/********************************************************************
* name : void StatusInquiry(void)
* description : 状态轮询
* Input : void
* Output : void
* Return : void
********************************************************************/
void StatusInquiry(ElectrodeStatusInfo_e Electrodestatus)
{
uint8_t j = 2;
static uint8_t aTxBuffer[10] = {0};
uint32_t ulCrc = 0;
uint16_t Length = 10;
/* 帧头、帧尾*/
aTxBuffer[HeadOffset] = 0xAA;
aTxBuffer[DataLengthOffset] = 0x06;
aTxBuffer[FunctionCodeOffset] = STATUS_INQUIRY;
aTxBuffer[ChannelNumOffset] = 0x00;
aTxBuffer[ElectrodeStatusOffset] = 0x01;
aTxBuffer[StimStatusOffset] = ChannelStimStateInfo_t.Ramp_e;
aTxBuffer[StimCurrentOffset] = ChannelStimStateInfo_t.ucCurrent;
aTxBuffer[ResetOffset] = 0x00;
aTxBuffer[TailOffset] = 0x55;
/* 校验和 */
for(uint8_t i = 0; i < 6; i++)
{
ulCrc += aTxBuffer[j];
j++;
}
aTxBuffer[DataCrcOffset] = (uint8_t)ulCrc;
ble_nus_data_send(&m_nus, aTxBuffer, &Length, m_conn_handle);
}
/********************************************************************
* name : uint8_t caculate_sum_check(void)
* description : 运行校验和计算
* Input : typeStructHeader数据结构头部
typeStructLength数据结构长度
* Output : void
* Return : 计算结果
********************************************************************/
uint8_t caculate_sum_check(uint8_t *typeStructHeader, uint8_t typeStructLength)
{
uint8_t sum = 0;
uint8_t * typeStructCMD = &typeStructHeader[2];
for (uint8_t i = 0; i < typeStructLength; i++)
{
sum += typeStructCMD[i];
}
return sum&0xFF;
}
/// @brief 运行状态回复
reply_run_status_t replyRunStatus = {
.frameHeader = FRAME_HEADER,
.frameLength = sizeof(replyRunStatus) - 4, //减去帧头、帧长度、校验和、帧尾
.functionCode = RUN_ROLL,
.myNumber = 0x01,
.channel = 0x01,
.checkSum = 0,
.frameTail = FRAME_TAIL
};
/********************************************************************
* name : void RunRoll(void)
* description : 运行轮询
* Input : void
* Output : void
* Return : void
********************************************************************/
void RunRoll(void)
{
uint32_t ulCrc = 0;
uint16_t Length = sizeof(replyRunStatus);
replyRunStatus.ChargeState = ChargeState;
replyRunStatus.BatteryLevelA = Battery_Percentage;
replyRunStatus.checkSum = (uint8_t)caculate_sum_check((uint8_t *)&replyRunStatus, replyRunStatus.frameLength);
ble_nus_data_send(&m_nus, &replyRunStatus.frameHeader, &Length, m_conn_handle);
}
/********************************************************************
* name : void CheckVersion(void)
* description : 版本查询
* Input : void
* Output : void
* Return : void
********************************************************************/
check_version_t checkVersion = {
.frameHeader = FRAME_HEADER,
.frameLength = sizeof(checkVersion) - 4, //减去帧头、帧长度、校验和、帧尾
.functionCode = VERSION_INQUIRY,
.myNumber = 0x01,
.channel = 0x01,
.checkSum = 0,
.frameTail = FRAME_TAIL
}
void CheckVersion(void)
{
uint8_t j = 2;
static uint8_t aTxBuffer[21] = {0};
uint32_t ulCrc = 0;
uint16_t Length = 21;
/* 帧头、帧尾*/
aTxBuffer[HeadOffset] = 0xAA;
aTxBuffer[DataLengthOffset] = 0x11;
aTxBuffer[FunctionCodeOffset] = VERSION_INQUIRY;
aTxBuffer[ChannelNumOffset] = 0x00;
aTxBuffer[20] = 0x55;
/* 协议中规定先发送高字节,后发送低字节 */
aTxBuffer[4] = VersionDes[0];
aTxBuffer[5] = VersionDes[1];
aTxBuffer[6] = VersionDes[2];
aTxBuffer[7] = VersionDes[3];
aTxBuffer[8] = VersionDes[4];
aTxBuffer[9] = VersionDes[5];
aTxBuffer[10] = VersionDes[6];
aTxBuffer[11] = VersionDes[7];
aTxBuffer[12] = 0x00;
aTxBuffer[13] = 0x00;
aTxBuffer[14] = 0x00;
aTxBuffer[15] = 0x00;
aTxBuffer[16] = 0x00;
aTxBuffer[17] = 0x00;
aTxBuffer[18] = 0x00;
/* 校验和 */
for(uint8_t i = 0; i < 17; i++)
{
ulCrc += aTxBuffer[j];
j++;
}
aTxBuffer[19] = (uint8_t)ulCrc;
ble_nus_data_send(&m_nus, aTxBuffer, &Length, m_conn_handle);
}
/********************************************************************
* name : void EegDataSend(void)
* description : 发送肌电数据
* Input : void
* Output : void
* Return : void
********************************************************************/
void EegDataSend(void)
{
uint8_t j = 2;
static uint8_t aTxBuffer[EEG_DATA_LENGTH+6] = {0};
uint32_t ulCrc = 0;
uint16_t Length = EEG_DATA_LENGTH+6;
ret_code_t err_code;
/* 帧头、帧尾*/
aTxBuffer[HeadOffset] = 0xAA;
aTxBuffer[DataLengthOffset] = 0x34;
aTxBuffer[FunctionCodeOffset] = EEG_DATA;
aTxBuffer[ChannelNumOffset] = 0x00;
aTxBuffer[EEG_DATA_LENGTH+5] = 0x55;
/* 协议中规定先发送高字节,后发送低字节 */
memcpy(&aTxBuffer[4],aEEGData,EEG_DATA_LENGTH);
/* 校验和 */
for(uint8_t i = 0; i < EEG_DATA_LENGTH+2; i++)
{
ulCrc += aTxBuffer[j];
j++;
}
aTxBuffer[EEG_DATA_LENGTH+4] = (uint8_t)ulCrc;
ble_nus_data_send(&m_nus, aTxBuffer, &Length, m_conn_handle);
}
/********************************************************************
* name : void UpdateCurrent(uint8_t CurrentSend)
* description : 同步电流
* Input : void
* Output : void
* Return : void
********************************************************************/
void UpdateCurrent(uint8_t CurrentSend)
{
uint8_t j = 2;
uint8_t aTxBuffer[8] = {0};
uint32_t ulCrc = 0;
uint16_t Length = 8;
/* 帧头、帧尾*/
aTxBuffer[HeadOffset] = 0xAA;
aTxBuffer[DataLengthOffset] = 0x04;
aTxBuffer[FunctionCodeOffset] = CURRENT_CONTROL;
aTxBuffer[ChannelNumOffset] = 0x00;
aTxBuffer[7] = 0x55;
/* 协议中规定先发送高字节,后发送低字节 */
aTxBuffer[4] = CurrentSend;
aTxBuffer[5] = 0x01; // 01代表蓝牙设备发送
/* 校验和 */
for(uint8_t i = 0; i < 4; i++)
{
ulCrc += aTxBuffer[j];
j++;
}
aTxBuffer[6] = (uint8_t)ulCrc;
ble_nus_data_send(&m_nus, aTxBuffer, &Length, m_conn_handle);
}
void user_ble_or_uart_send(char * txBufferP, uint16_t Length)
{
ble_nus_data_send(&m_nus, txBufferP, &Length, m_conn_handle);
}
/********************************************************************
* name : void ble_send_rms_data(uint16_t rms_data)
* description : 发送肌电数据到主机
* Input : uint16_t rms_data肌电数据
* Output : void
* Return :
********************************************************************/
void ble_send_rms_data(uint16_t rms_data)
{
uint32_t TempSum = 0;
static uint8_t sendCnt = 0;
if(sendCnt < RMS_USER_DATA_LENGTH)
{
rmsData.rmsDataBuffer[sendCnt] = rms_data; //将rms_data存入rmsData的rmsDataBuffer数组中
sendCnt++;
if(sendCnt < RMS_USER_DATA_LENGTH)
return ; //如果发送次数小于RMS_USER_DATA_LENGTH则不发送
}
sendCnt = 0; //发送次数达到RMS_USER_DATA_LENGTH后重置发送次数
rmsData.myNumber = 1;
//rmsData.myNumber++;
uint8_t * sumCheckStartP = (uint8_t *)&rmsData.functionCode;
for(uint8_t i = 0; i < rmsData.frameLength; i++)
{
TempSum += sumCheckStartP[i];
}
rmsData.checkSum = TempSum & 0xff;
user_ble_or_uart_send((uint8_t *)&rmsData, sizeof(rmsData)); //145us
}
/********************************************************************
* name : void StateUpLoad(AdapterStateInfo_e AdapterStateTemp , ElectrodeStatusInfo_e ElectrodeStatusTemp)
* description : 电极片和适配器状态上传
* Input : AdapterStateInfo_e AdapterStateTemp , ElectrodeStatusInfo_e ElectrodeStatusTemp
* Output : void
* Return : void
********************************************************************/
void StateUpLoad(AdapterStateInfo_e AdapterStateTemp , ElectrodeStatusInfo_e ElectrodeStatusTemp)
{
uint8_t j = 2;
uint8_t aTxBuffer[8] = {0};
uint32_t ulCrc = 0;
uint16_t Length = 8;
AdapterStateInfo_e AdapterStateIn;
ElectrodeStatusInfo_e ElectrodeStatusIn;
AdapterStateIn = AdapterStateTemp;
ElectrodeStatusIn = ElectrodeStatusTemp;
/* 帧头、帧尾*/
aTxBuffer[HeadOffset] = 0xAA;
aTxBuffer[DataLengthOffset] = 0x04;
aTxBuffer[FunctionCodeOffset] = STATE_UPLOAD;
aTxBuffer[ChannelNumOffset] = 0x00;
aTxBuffer[7] = 0x55;
aTxBuffer[4] = AdapterStateIn; // 适配器连接状态
aTxBuffer[5] = ElectrodeStatusIn; // 电极片状态
if(ElectrodeStatusIn == ElectrodeFalloff)
{
ElectrodeStatusInfo = ElectrodeConnectted;
}
NRF_LOG_INFO("Electrodestatus = %d",ElectrodeStatusIn);
/* 校验和 */
for(uint8_t i = 0; i < 4; i++)
{
ulCrc += aTxBuffer[j];
j++;
}
aTxBuffer[6] = (uint8_t)ulCrc;
ble_nus_data_send(&m_nus, aTxBuffer, &Length, m_conn_handle);
}
/********************************************************************
* name : void UpdateControlStatus(uint8_t ControlStatus)
* description : 同步电流
* Input : void
* Output : void
* Return : void
********************************************************************/
void UpdateControlStatus(uint8_t ControlStatus)
{
uint8_t j = 2;
uint8_t aTxBuffer[8] = {0};
uint32_t ulCrc = 0;
uint16_t Length = 8;
/* 帧头、帧尾*/
aTxBuffer[HeadOffset] = 0xAA;
aTxBuffer[DataLengthOffset] = 0x04;
aTxBuffer[FunctionCodeOffset] = START_STOP_CONTROL;
aTxBuffer[ChannelNumOffset] = 0x00;
aTxBuffer[7] = 0x55;
/* 协议中规定先发送高字节,后发送低字节 */
aTxBuffer[4] = ControlStatus;
aTxBuffer[5] = 0x01; // 01代表蓝牙设备发送
/* 校验和 */
for(uint8_t i = 0; i < 4; i++)
{
ulCrc += aTxBuffer[j];
j++;
}
aTxBuffer[6] = (uint8_t)ulCrc;
ble_nus_data_send(&m_nus, aTxBuffer, &Length, m_conn_handle);
}
/********************************************************************
* name : void SchemeQuery(uint8_t idMSB,uint8_t idLSB)
* description : 方案查询
* Input : void
* Output : void
* Return : void
********************************************************************/
void SchemeQuery(uint8_t idMSB,uint8_t idLSB)
{
uint8_t j = 2;
static uint8_t aTxBuffer[8] = {0};
uint32_t ulCrc = 0;
uint16_t Length = 8;
/* 帧头、帧尾*/
aTxBuffer[HeadOffset] = 0xAA;
aTxBuffer[DataLengthOffset] = 0x04;
aTxBuffer[FunctionCodeOffset] = SCHEME_QUERY;
aTxBuffer[ChannelNumOffset] = 0x00;
aTxBuffer[7] = 0x55;
/* 协议中规定先发送高字节,后发送低字节 */
aTxBuffer[4] = idMSB;
aTxBuffer[5] = idLSB;
/* 校验和 */
for(uint8_t i = 0; i < 4; i++)
{
ulCrc += aTxBuffer[j];
j++;
}
aTxBuffer[6] = (uint8_t)ulCrc;
ble_nus_data_send(&m_nus, aTxBuffer, &Length, m_conn_handle);
}
/********************************************************************
* name : void MACQuery(void)
* description : MAC地址查询
* Input : void
* Output : void
* Return : void
********************************************************************/
void MACQuery(void)
{
uint8_t j = 2;
static uint8_t aTxBuffer[12] = {0};
uint32_t ulCrc = 0;
uint16_t Length = 12;
/* 帧头、帧尾*/
aTxBuffer[HeadOffset] = 0xAA;
aTxBuffer[DataLengthOffset] = 0x08;
aTxBuffer[FunctionCodeOffset] = MAC_QUERY;
aTxBuffer[ChannelNumOffset] = 0x00;
aTxBuffer[11] = 0x55;
aTxBuffer[4] = BLE_MAC[5];
aTxBuffer[5] = BLE_MAC[4];
aTxBuffer[6] = BLE_MAC[3];
aTxBuffer[7] = BLE_MAC[2];
aTxBuffer[8] = BLE_MAC[1];
aTxBuffer[9] = BLE_MAC[0];
/* 校验和 */
for(uint8_t i = 0; i < 8; i++)
{
ulCrc += aTxBuffer[j];
j++;
}
aTxBuffer[10] = (uint8_t)ulCrc;
ble_nus_data_send(&m_nus, aTxBuffer, &Length, m_conn_handle);
}
/********************************************************************
* name : void JudgeLedMode(void)
* description :
* Input : void
* Output : void
* Return : void
********************************************************************/
void JudgeLedMode(void)
{
}
void DisconnectControl(void)
{
StopManage();
}
/*************************** END OF FILE ***************************/

482
app/Src/timer.c Normal file
View File

@@ -0,0 +1,482 @@
/********************************************************************
Copyright (c) 2021 Xiangyu Medical Co.Ltd. All rights reserved.
FileName : timer.c
Author : zhangdawei
Version : V1.0
Date :
Note :
History :
********************************************************************/
/* Includes ------------------------------------------------------*/
#include "nrf_drv_pwm.h"
#include "drv_uart.h"
#include "IoControl.h"
#include "timer.h"
#include "nrf_delay.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_gpiote.h"
/* Private define ------------------------------------------------*/
/* 定义APP定时器 */
APP_TIMER_DEF(detect_button_100ms);
#define DETECT_BUTTON APP_TIMER_TICKS(100)
/* Private typedef -----------------------------------------------*/
//定义名称为m_pwm_add的驱动程序实例
static nrfx_pwm_t m_pwm_dac = NRFX_PWM_INSTANCE(1);
static nrfx_pwm_t m_pwm0 = NRFX_PWM_INSTANCE(0);
static nrfx_pwm_t m_pwm2 = NRFX_PWM_INSTANCE(2);
/* Private constants ---------------------------------------------*/
/* Private variables ---------------------------------------------*/
/* 呼吸灯占空比 */
uint16_t m_step = 150;
/* 存放RunLED占空比的数组 */
static nrf_pwm_values_common_t seq3_values[M_STEP*2];
//定义PWM序列该序列必须位于RAM中因此要定义为static类型
static nrf_pwm_values_common_t dac_values[] = {CYCLE_CALUE}; //14
static nrf_pwm_values_wave_form_t seq0_current_values[] =
{
10000 - 600,
10000 - 400,
10000 - 200,
10000 //100Hz
};
static nrf_pwm_values_wave_form_t seq2_current_values[] =
{
800 - 125,
800,
800,
800 //20khz
};
E_WORK_STATE eRmsState = E_RMS_STOP;
/* Private function prototypes -----------------------------------*/
/* Public constants ----------------------------------------------*/
/* Public variables ----------------------------------------------*/
/********************************************************************
* name : void acquisition_start(void)
* description : 开始采集肌电
* Input : void
* Output : void
* Return :
********************************************************************/
void acquisition_start(void)
{
eRmsState = E_RMS_START;
// rms_saadc_init();
nrf_gpio_pin_set(SAMPLE_POWER_PIN);
open_acquisition_relay();
timer3_rms_start();
}
/********************************************************************
* name : void acquisition_stop(void)
* description : 停止肌电采集
* Input : void
* Output : void
* Return :
********************************************************************/
void acquisition_stop(void)
{
timer3_rms_stop();
eRmsState = E_RMS_STOP;
nrf_gpio_pin_clear(SAMPLE_POWER_PIN);
close_acquisition_relay();
}
/********************************************************************
* name : void stimulate_start(void)
* description : 开始刺激
* Input : void
* Output : void
* Return :
********************************************************************/
void stimulate_start(void)
{
acquisition_stop(); //先停止肌电采集
// stimulate_stop(); //停止刺激,重新开始刺激
eRmsState = E_STIM_START;
open_stimulate_relay();
pwm0_play();
pwm2_play();
}
/********************************************************************
* name : void stimulate_stop(void)
* description : 停止刺激
* Input : void
* Output : void
* Return :
********************************************************************/
void stimulate_stop(void)
{
eRmsState = E_STIM_STOP;
close_stimulate_relay();
}
/********************************************************************
* name : static void detect_button(void)
* description : 开关机按键检测处理
* Input : void
* Output : void
* Return :
********************************************************************/
static void detect_button(void)
{
static uint8_t CountValue=0;
if((Bit_RESET == nrf_gpio_pin_read(KEY_PWR_SWITICH))&&((ChargeState == Uncharged))) //检测按键是否按下
{
CountValue++;
if(CountValue == 10)
{
if(POWER_CLOSE == DeviceState) //开机
{
DeviceState = POWER_OPEN;
nrf_gpio_pin_set(KEY_POWER); //锁定电源键给MCU供电
NRF_LOG_INFO("Open!");
StartAdv();
}
else if(POWER_OPEN == DeviceState) //关机
{
DeviceState = POWER_CLOSE;
acquisition_stop();
NRF_LOG_INFO("Close!");
nrf_gpio_pin_clear(KEY_POWER); //断电
}
}
}
else
{
CountValue = 0;
}
static uint16_t runBlinkCount = 0;
if(runBlinkCount == 0 && DeviceState == POWER_OPEN)
nrf_gpio_pin_set(LED_YELLOW);
else if(runBlinkCount == 1)
nrf_gpio_pin_clear(LED_YELLOW);
if(runBlinkCount++ > 8) //1S闪烁一次
{
runBlinkCount = 0;
}
}
/********************************************************************
* name : static void detect_charging_status(void)
* description : 充电状态检测处理
* Input : void
* Output : void
* Return :
********************************************************************/
static void detect_charging_status(void)
{
static uint8_t CountValue=0;
if((Bit_RESET == nrf_gpio_pin_read(CHG_MANAGER_CHG))) //检测按键是否按下
{
ChargeState = Charging;
}
else
{
ChargeState = Uncharged;
}
}
/********************************************************************
* name : static void Hundredms_handler(void * p_context)
* description : 检测按键是否长按函数100ms中断一次累计计数
* Input : void *p_context
* Output : void
* Return :
********************************************************************/
static void Hundredms_handler(void * p_context)
{
//防止编译器警告同时清晰地的表明p_context未使用而不是应用程序忽略了。如果应用
//程序需要使用p_context则不需要这行代码
UNUSED_PARAMETER(p_context);
/* 开机按键检测处理 */
detect_button();
/* 电池电量检测 */
CalculateBatteryPower();
/* 充电状态检测 */
detect_charging_status();
static uint16_t delay_open = 0;
if(POWER_OPEN == DeviceState && delay_open != 70 && delay_open++ >60 )
{
//acquisition_start();
stimulate_start();
delay_open = 70;
}
}
/********************************************************************
* name : void AppTimersInit(void)
* description : 初始化APP定时器模块、创建APP定时器
* Input : void
* Output : void
* Return :
********************************************************************/
void AppTimersInit(void)
{
ret_code_t err_code = app_timer_init();//初始化APP定时器模块
APP_ERROR_CHECK(err_code);
err_code = app_timer_create(&detect_button_100ms,
APP_TIMER_MODE_REPEATED,
Hundredms_handler);
APP_ERROR_CHECK(err_code);
}
/********************************************************************
* name : void ApplicationTimersStart(void)
* description : 启动已经创建的的APP定时器
* Input : void
* Output : void
* Return :
********************************************************************/
void ApplicationTimersStart(void)
{
ret_code_t err_code;
err_code = app_timer_start(detect_button_100ms, DETECT_BUTTON, NULL);
APP_ERROR_CHECK(err_code);
}
/********************************************************************
* name : void PwmDACInit(void)
* description : 控制DAC的pwm初始化
* Input : void
* Output : void
* Return :
********************************************************************/
void PwmDACInit(void)
{
//定义PWM初始化配置结构体并初始化参数
nrfx_pwm_config_t const config0 =
{
.output_pins =
{
PWM_DAC_GPIO, //通道0映射到P WM_DAC_GPIO空闲状态输出低电平
NRFX_PWM_PIN_NOT_USED , //通道1不使用
NRFX_PWM_PIN_NOT_USED , //通道2不使用
NRFX_PWM_PIN_NOT_USED //通道3不使用
},
.irq_priority = APP_IRQ_PRIORITY_LOWEST,//中断优先级
.base_clock = NRF_PWM_CLK_1MHz, //PWM时钟频率设置为1MHz
.count_mode = NRF_PWM_MODE_UP, //向上计数模式
.top_value = CYCLE_CALUE, //该值为周期值
.load_mode = NRF_PWM_LOAD_COMMON, //通用模式
.step_mode = NRF_PWM_STEP_AUTO //序列中的周期自动推进
};
APP_ERROR_CHECK(nrfx_pwm_init(&m_pwm_dac, &config0, NULL));//初始化PWM
}
/********************************************************************
* name : void PwmDACPlay(void)
* description : 播放充能PWM
* Input : void
* Output : void
* Return :
********************************************************************/
void PwmDACPlay(void)
{
//定义PWM播放序列播放序列包含了PWM序列的起始地址、大小和序列播放控制描述
nrf_pwm_sequence_t const seq =
{
.values.p_common = dac_values,//指向PWM序列
.length = NRF_PWM_VALUES_LENGTH(dac_values),//PWM序列中包含的周期个数
.repeats = 0, //序列中周期重复次数为0
.end_delay = 0 //序列后不插入延时
};
//启动PWM序列播放flags设置为NRFX_PWM_FLAG_LOOP序列播放完成后自动触发任务重新播放
(void)nrfx_pwm_simple_playback(&m_pwm_dac, &seq, 1, NRFX_PWM_FLAG_LOOP);
}
void SetPWMValues(uint16_t tempvalue)
{
*dac_values = CYCLE_CALUE - tempvalue;
}
/********************************************************************
* name : void PwmDACStop(void)
* description : 停止播放充能PWM
* Input : void
* Output : void
* Return :
********************************************************************/
void PwmDACStop(void)
{
nrfx_pwm_stop(&m_pwm_dac,1);
}
/********************************************************************
* name : void pwm0_common_init(void)
* description : 电流控制PWM初始化
* Input : void
* Output : void
* Return :
********************************************************************/
void pwm0_common_init(void)
{
//定义PWM初始化配置结构体并初始化参数
nrfx_pwm_config_t const config0 =
{
.output_pins =
{
BASE_WAVE_06_PIN,
BASE_WAVE_07_PIN,
BASE_WAVE_08_PIN,
NRFX_PWM_PIN_NOT_USED
},
.irq_priority = APP_IRQ_PRIORITY_LOWEST,//中断优先级
.base_clock = NRF_PWM_CLK_1MHz, //PWM时钟频率设置为16MHz
.count_mode = NRF_PWM_MODE_UP, //向上计数模式
.top_value = 0, //使用波形装载模式时,该值被忽略
.load_mode = NRF_PWM_LOAD_WAVE_FORM, //独立装载模式
.step_mode = NRF_PWM_STEP_AUTO //序列中的周期自动推进
};
//初始化PWM
APP_ERROR_CHECK(nrfx_pwm_init(&m_pwm0, &config0, NULL));
/////////////
//pwm0_play();
}
/********************************************************************
* name : void pwm0_play(void)
* description : 电流控制PWM开始播放
* Input : void
* Output : void
* Return :
********************************************************************/
void pwm0_play(void)
{
//定义PWM播放序列播放序列包含了PWM序列的起始地址、大小和序列播放控制描述
nrf_pwm_sequence_t const seq0 =
{
.values.p_wave_form = seq0_current_values,//指向PWM序列
.length = NRF_PWM_VALUES_LENGTH(seq0_current_values),//PWM序列中包含的周期个数
.repeats = 0, //序列中周期重复次数为0
.end_delay = 0 //序列后不插入延时
};
//启动PWM序列播放flags设置为NRFX_PWM_FLAG_LOOP序列播放完成后自动触发任务重新播放
//如改为NRFX_PWM_FLAG_STOP则播放结束后PWM停止
(void)nrfx_pwm_simple_playback(&m_pwm0, &seq0, 1,
NRFX_PWM_FLAG_LOOP);
}
void pwm0_stop(void)
{
nrfx_pwm_stop(&m_pwm0, 1);
}
/********************************************************************
* name : void pwm2_common_init(void)
* description : 电流控制PWM初始化
* Input : void
* Output : void
* Return :
********************************************************************/
void pwm2common_init(void)
{
//定义PWM初始化配置结构体并初始化参数
nrfx_pwm_config_t const config0 =
{
.output_pins =
{
BOOST_VOLTAGE_CONTROL_PIN,
NRFX_PWM_PIN_NOT_USED,
NRFX_PWM_PIN_NOT_USED,
NRFX_PWM_PIN_NOT_USED
},
.irq_priority = APP_IRQ_PRIORITY_LOWEST,//中断优先级
.base_clock = NRF_PWM_CLK_16MHz, //PWM时钟频率设置为16MHz
.count_mode = NRF_PWM_MODE_UP, //向上计数模式
.top_value = 0, //使用波形装载模式时,该值被忽略
.load_mode = NRF_PWM_LOAD_WAVE_FORM, //独立装载模式
.step_mode = NRF_PWM_STEP_AUTO //序列中的周期自动推进
};
//初始化PWM
APP_ERROR_CHECK(nrfx_pwm_init(&m_pwm2, &config0, NULL));
/////////////
//pwm2_play();
}
/********************************************************************
* name : void pwm2_play(void)
* description : 电流控制PWM开始播放
* Input : void
* Output : void
* Return :
********************************************************************/
void pwm2_play(void)
{
//定义PWM播放序列播放序列包含了PWM序列的起始地址、大小和序列播放控制描述
nrf_pwm_sequence_t const seq2 =
{
.values.p_wave_form = seq2_current_values,//指向PWM序列
.length = NRF_PWM_VALUES_LENGTH(seq2_current_values),//PWM序列中包含的周期个数
.repeats = 0, //序列中周期重复次数为0
.end_delay = 0 //序列后不插入延时
};
//启动PWM序列播放flags设置为NRFX_PWM_FLAG_LOOP序列播放完成后自动触发任务重新播放
//如改为NRFX_PWM_FLAG_STOP则播放结束后PWM停止
(void)nrfx_pwm_simple_playback(&m_pwm2, &seq2, 1,
NRFX_PWM_FLAG_LOOP);
}
void pwm2_stop(void)
{
nrfx_pwm_stop(&m_pwm2, 1);
}
/********************************************************************
* name : void GpioteInit(void)
* description : 刺激的任务GPIOTE初始化
* Input : void
* Output : void
* Return :
********************************************************************/
void GpioteInit(void)
{
}
/********************************************************************
* name : void PPIPwmInit(void)
* description : 刺激PWM的PPI初始化
* Input : void
* Output : void
* Return :
********************************************************************/
void PPIPwmInit(void)
{
}
/*************************** END OF FILE ***************************/

32
app/Src/user_config.c Normal file
View File

@@ -0,0 +1,32 @@
/********************************************************************
Copyright (c) 2025 Xiangyu Medical Co.Ltd. All rights reserved.
FileName : spi.c
Author : xiaozhengsheng
Version : V1.0
Date :
Note :
History :
********************************************************************/
/* Includes ------------------------------------------------------*/
#include "user_config.h"
//Log需要引用的头文件
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
version_t softVersion={
.major = 1,
.minor = 0,
.patch = 0, // 修订号 (.0)
.build = 0, // 构建号 (.0)
.testVersion = {"alpha1-1"} //内部测试版本正式发布需要填0alpha内部测试
};
char softWareVersion[SOFT_VERSION_MAX_LENGTH] = {0}; //软件版本号
void read_config_user_config(void)
{
snprintf(softWareVersion, SOFT_VERSION_MAX_LENGTH, "V%d.%d.%d.%d-%s", softVersion.major, softVersion.minor, softVersion.patch, softVersion.build, softVersion.testVersion);
NRF_LOG_INFO("Software Version: %s", softWareVersion);
NRF_LOG_INFO("product model: HL-PDJ-1");
}

883
app/main.c Normal file
View File

@@ -0,0 +1,883 @@
/****************************************Copyright (c)************************************************
** [翔宇医疗]
**--------------File Info-----------------------------------------------------------------------------
** File name : main.c
** Last modified Date:
** Last Version :
** Descriptions : 使用的SDK版本-SDK_17.0.2
**----------------------------------------------------------------------------------------------------
** Created by :
** Created date :
** Version : 1.0
** Descriptions : 2021.4.6添加配对管理器功能在配对管理处理事件中添加PM_EVT_CONN_SEC_CONFIG_REQ修改参数
实现取消配对后依然可以再次连接
2021.4.7添加广播3分钟后芯片休眠操作广播间隔调整为187.5ms
在BLE事件处理函数中添加pm_handler_secure_on_connection函数实现连接成功后就绑定
2021.4.9在GATT初始化中加入nrf_ble_gatt_att_mtu_periph_set()函数设置默认MTU大小
2021.4.12:在主函数中加入PWM初始化函数以及在定时器中调用PWM开始播放函数
2021.5.12将最大连接间隔改为300ms改善了在连接过程中交换MTU不成功的情况
**---------------------------------------------------------------------------------------------------*/
//引用的C库头文件
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
//APP定时器需要引用的头文件
#include "bsp_btn_ble.h"
//电源管理需要引用的头文件
#include "nrf_pwr_mgmt.h"
//SoftDevice handler configuration需要引用的头文件
#include "nrf_sdh.h"
#include "nrf_sdh_soc.h"
#include "nrf_sdh_ble.h"
//排序写入模块需要引用的头文件
#include "nrf_ble_qwr.h"
//GATT需要引用的头文件
#include "nrf_ble_gatt.h"
//连接参数协商需要引用的头文件
#include "ble_conn_params.h"
//广播需要引用的头文件
#include "ble_advdata.h"
#include "ble_advertising.h"
//串口透传需要引用的头文件
#include "drv_uart.h"
//引用FDS头文件
#include "fds.h"
//配对管理器包含头文件
#include "peer_manager.h"
#include "peer_manager_handler.h"
//WDT头文件
#include "nrfx_wdt.h"
#include "nrf_drv_clock.h"
#include "IoControl.h"
#include "IIR.h"
/***********************************************/
#define DEVICE_NAME "HL-PDJ-A" // 设备名称字符串
#define UARTS_SERVICE_UUID_TYPE BLE_UUID_TYPE_BLE // 串口透传服务UUID类型厂商自定义UUID
#define MIN_CONN_INTERVAL MSEC_TO_UNITS(20, UNIT_1_25_MS) // 最小连接间隔 (0.1 秒)
#define MAX_CONN_INTERVAL MSEC_TO_UNITS(20, UNIT_1_25_MS) // 最大连接间隔 (0.2 秒)
#define SLAVE_LATENCY 0 // 从机延迟
#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) // 监督超时(4 秒)
#define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000) // 定义首次调用sd_ble_gap_conn_param_update()函数更新连接参数延迟时间5秒
#define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000) // 定义每次调用sd_ble_gap_conn_param_update()函数更新连接参数的间隔时间30秒
#define MAX_CONN_PARAMS_UPDATE_COUNT 3 // 定义放弃连接参数协商前尝试连接参数协商的最大次数3次
#define APP_ADV_INTERVAL 160 // 广播间隔 (100 ms)单位0.625 ms
#define APP_ADV_DURATION 0 // 广播持续时间单位10ms。设置为0表示不超时
#define SEC_PARAM_BOND 1 //是否支持绑定1支持0不支持
#define SEC_PARAM_MITM 0 //是否支持MITM保护1支持0不支持
#define SEC_PARAM_LESC 0 //是否使用安全连接配对LESC1使用LESC0使用传统配对
#define SEC_PARAM_KEYPRESS 0 //是否生成按键通知1生成0不生成
#define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_NONE //IO能力无输入/输出能力
#define SEC_PARAM_OOB 0 //是否支持OOB1支持0不支持
#define SEC_PARAM_MIN_KEY_SIZE 7 //最小加密密钥大小
#define SEC_PARAM_MAX_KEY_SIZE 16 //最大加密密钥大小
#define APP_BLE_OBSERVER_PRIO 3 //应用程序BLE事件监视者优先级应用程序不能修改该数值
#define APP_BLE_CONN_CFG_TAG 1 //SoftDevice BLE配置标志
//用于stack dump的错误代码可以用于栈回退时确定堆栈位置
#define DEAD_BEEF 0xDEADBEEF
//定义文件ID和该文件包含的记录的KEY
#define DEVICE_FILE (0x1000)//文件ID
#define DEVICE_SCHEME_KEY (0x1001)//记录KEY该记录存放的文件ID=0X1001
ble_gap_addr_t addr_ios;
uint8_array_t address_ios;
// 包含参数信息的记录
fds_record_t const m_SchemePara =
{
.file_id = DEVICE_FILE,
.key = DEVICE_SCHEME_KEY,
.data.p_data = &SchemePara,
//记录的长度必须以4字节为单位
.data.length_words = (sizeof(SchemePara) + 3) / sizeof(uint32_t),
};
//BLE串口透传例程中服务UUID列表
static ble_uuid_t m_adv_uuids[] =
{
{BLE_UUID_NUS_SERVICE,UARTS_SERVICE_UUID_TYPE}
};
//保存申请的喂狗通道
nrfx_wdt_channel_id m_channel_id;
/*必须的观察者函数*/
NRF_BLE_GATT_DEF(m_gatt); //定义名称为m_gatt的GATT模块实例
NRF_BLE_QWR_DEF(m_qwr); //定义一个名称为m_qwr的排队写入实例
BLE_ADVERTISING_DEF(m_advertising); //定义名称为m_advertising的广播模块实例
void WdtFeed(void);
//GATT事件处理函数
void gatt_evt_handler(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_t const * p_evt)
{
//如果是MTU交换事件
if ((m_conn_handle == p_evt->conn_handle) && (p_evt->evt_id == NRF_BLE_GATT_EVT_ATT_MTU_UPDATED)) //如果主句发起MTU升级请求
{
//设置串口透传服务的有效数据长度MTU-opcode-handle
m_ble_nus_max_data_len = p_evt->params.att_mtu_effective - OPCODE_LENGTH - HANDLE_LENGTH;///247字节
NRF_LOG_INFO("Data len is set to 0x%X(%d)", m_ble_nus_max_data_len, m_ble_nus_max_data_len);
}
NRF_LOG_DEBUG("ATT MTU exchange completed. central 0x%x peripheral 0x%x",
p_gatt->att_mtu_desired_central,
p_gatt->att_mtu_desired_periph);
}
//初始化日志打印模块
static void log_init(void)
{
//初始化log程序模块
ret_code_t err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);
//设置log输出终端根据sdk_config.h中的配置设置输出终端为UART或者RTT
NRF_LOG_DEFAULT_BACKENDS_INIT();
}
//GAP Generic Access Profile参数初始化该函数配置需要的GAP参数包括设备名称外观特征、首选连接参数
static void gap_params_init(void)
{
ret_code_t err_code;
//定义连接参数结构体变量
ble_gap_conn_params_t gap_conn_params;
ble_gap_conn_sec_mode_t sec_mode;
//设置GAP的安全模式,即设置设备名称特征的写权限这里设置的是安全模式1等级1即无安全性
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
//设置GAP设备名称
err_code = sd_ble_gap_device_name_set(&sec_mode,
(const uint8_t *)DEVICE_NAME,
strlen(DEVICE_NAME));
APP_ERROR_CHECK(err_code);
//如果需要设置外观特征,在这里使用如下的代码设置
/* err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_);
APP_ERROR_CHECK(err_code); */
//设置首选连接参数设置前先清零gap_conn_params
memset(&gap_conn_params, 0, sizeof(gap_conn_params));
gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;//最小连接间隔
gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;//最小连接间隔
gap_conn_params.slave_latency = SLAVE_LATENCY; //从机延迟
gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT; //监督超时
//调用协议栈API sd_ble_gap_ppcp_set配置GAP参数
err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
APP_ERROR_CHECK(err_code);
}
//初始化GATT程序模块
static void gatt_init(void)
{
//初始化GATT程序模块
ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, gatt_evt_handler);
//检查函数返回的错误代码
APP_ERROR_CHECK(err_code);
//设置服务端的默认MTU大小
err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE);
APP_ERROR_CHECK(err_code);
}
//空闲状态处理函数(该函数需要放到主循环里面执行)。如果没有挂起的日志操作,则睡眠直到下一个事件发生后唤醒系统
static void idle_state_handle(void)
{
//处理挂起的log
if (NRF_LOG_PROCESS() == false)
{
//运行电源管理
nrf_pwr_mgmt_run();
}
}
//删除绑定信息
static void delete_bonds(void)
{
ret_code_t err_code;
err_code = pm_peers_delete();
APP_ERROR_CHECK(err_code);
}
//初始化电源管理模块
static void power_management_init(void)
{
ret_code_t err_code;
//初始化电源管理
err_code = nrf_pwr_mgmt_init();
//检查函数返回的错误代码
APP_ERROR_CHECK(err_code);
}
//连接参数协商模块错误处理事件参数nrf_error包含了错误代码通过nrf_error可以分析错误信息
static void conn_params_error_handler(uint32_t nrf_error)
{
//检查错误代码
APP_ERROR_HANDLER(nrf_error);
}
//连接参数协商模块事件处理函数
static void on_conn_params_evt(ble_conn_params_evt_t * p_evt)
{
ret_code_t err_code;
//判断事件类型,根据事件类型执行动作
//连接参数协商失败
if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
{
err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
APP_ERROR_CHECK(err_code);
}
//连接参数协商成功
if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_SUCCEEDED)
{
//功能代码;
}
}
//连接参数协商模块初始化(用于启动和执行连接参数协商规程)
static void conn_params_init(void)
{
ret_code_t err_code;
//定义连接参数协商模块初始化结构体
ble_conn_params_init_t cp_init;
//配置之前先清零
memset(&cp_init, 0, sizeof(cp_init));
//设置为NULL从主机获取连接参数
cp_init.p_conn_params = NULL;
//连接或启动通知到首次发起连接参数更新请求之间的时间设置为5秒
cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
//每次调用sd_ble_gap_conn_param_update()函数发起连接参数更新请求的之间的间隔时间设置为30秒
cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY;
//放弃连接参数协商前尝试连接参数协商的最大次数设置为3次
cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT;
//连接参数更新从连接事件开始计时
cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID;
//连接参数更新失败不断开连接
cp_init.disconnect_on_fail = false;
//注册连接参数更新事件句柄
cp_init.evt_handler = on_conn_params_evt;
//注册连接参数更新错误事件句柄
cp_init.error_handler = conn_params_error_handler;
//调用库函数(以连接参数更新初始化结构体为输入参数)初始化连接参数协商模块
err_code = ble_conn_params_init(&cp_init);
APP_ERROR_CHECK(err_code);
}
//BLE事件处理函数
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
ret_code_t err_code = NRF_SUCCESS;
//连接建立后立即启动安全当接收到连接建立事件BLE_GAP_EVT_CONNECTED时启动安全性
pm_handler_secure_on_connection(p_ble_evt);
//判断BLE事件类型根据事件类型执行相应操作
switch (p_ble_evt->header.evt_id)
{
//断开连接事件
case BLE_GAP_EVT_DISCONNECTED:
//打印提示信息
NRF_LOG_INFO("Disconnected.");
DeviceConnectState = DisconnectState;
DisconnectControl();
break;
//连接事件
case BLE_GAP_EVT_CONNECTED:
NRF_LOG_INFO("Connected.");
DeviceConnectState = ConnectState;
DisconnectControl();
//保存连接句柄
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
//将连接句柄分配给排队写入实例,分配后排队写入实例和该连接关联,这样,当有
//多个连接的时候,通过关联不同的排队写入实例,很方便单独处理各个连接
err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
APP_ERROR_CHECK(err_code);
break;
//PHY更新事件
case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
{
NRF_LOG_DEBUG("PHY update request.");
ble_gap_phys_t const phys =
{
.rx_phys = BLE_GAP_PHY_AUTO,
.tx_phys = BLE_GAP_PHY_AUTO,
};
//响应PHY更新规程
err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
APP_ERROR_CHECK(err_code);
} break;
//GATT客户端超时事件
case BLE_GATTC_EVT_TIMEOUT:
NRF_LOG_DEBUG("GATT Client Timeout.");
//断开当前连接
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
break;
//GATT服务器超时事件
case BLE_GATTS_EVT_TIMEOUT:
NRF_LOG_DEBUG("GATT Server Timeout.");
//断开当前连接
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
break;
default:
break;
}
}
//初始化BLE协议栈
static void ble_stack_init(void)
{
ret_code_t err_code;
//请求使能SoftDevice该函数中会根据sdk_config.h文件中低频时钟的设置来配置低频时钟
err_code = nrf_sdh_enable_request();
APP_ERROR_CHECK(err_code);
//定义保存应用程序RAM起始地址的变量
uint32_t ram_start = 0;
//使用sdk_config.h文件的默认参数配置协议栈获取应用程序RAM起始地址保存到变量ram_start
err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
APP_ERROR_CHECK(err_code);
//使能BLE协议栈
err_code = nrf_sdh_ble_enable(&ram_start);
APP_ERROR_CHECK(err_code);
//注册BLE事件回调函数
NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}
//广播事件处理函数
static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
{
ret_code_t err_code;
//判断广播事件类型
switch (ble_adv_evt)
{
//快速广播启动事件:快速广播启动后会产生该事件
case BLE_ADV_EVT_FAST:
NRF_LOG_INFO("Fast advertising.");
//设置广播指示灯为正在广播D1指示灯闪烁
// err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
// APP_ERROR_CHECK(err_code);
break;
//广播IDLE事件广播超时后会产生该事件
case BLE_ADV_EVT_IDLE:
//断电
NRF_LOG_INFO("BLE_ADV_EVT_IDLE");
nrf_gpio_pin_clear(KEY_POWER);
break;
default:
break;
}
}
//广播初始化
static void advertising_init(void)
{
uint32_t err_code;
//定义广播初始化配置结构体变量
ble_advertising_init_t init;
err_code = sd_ble_gap_addr_get(&addr_ios);
APP_ERROR_CHECK(err_code);
address_ios.size = 6;
address_ios.p_data = addr_ios.addr;
ble_advdata_manuf_data_t test;
test.company_identifier = 0x1122;
test.data = address_ios;
//配置之前先清零
memset(&init, 0, sizeof(init));
//设备名称类型:全名
init.advdata.name_type = BLE_ADVDATA_FULL_NAME;
//是否包含外观:包含
init.advdata.include_appearance = false;
//包含ble设备地址
// init.advdata.include_ble_device_addr = true;
init.advdata.p_manuf_specific_data = &test;
init.srdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids)/sizeof(m_adv_uuids[0]);
init.srdata.uuids_complete.p_uuids = m_adv_uuids;
//Flag:一般可发现模式不支持BR/EDR
init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
//设置广播模式为快速广播
init.config.ble_adv_fast_enabled = true;
//设置广播间隔和广播持续时间
init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
init.config.ble_adv_fast_timeout = APP_ADV_DURATION;
//广播事件回调函数
init.evt_handler = on_adv_evt;
//初始化广播
err_code = ble_advertising_init(&m_advertising, &init);
APP_ERROR_CHECK(err_code);
//设置广播配置标记。APP_BLE_CONN_CFG_TAG是用于跟踪广播配置的标记这是为未来预留的一个参数在将来的SoftDevice版本中
//可以使用sd_ble_gap_adv_set_configure()配置新的广播配置
//当前SoftDevice版本S132 V7.2.0版本支持的最大广播集数量为1因此APP_BLE_CONN_CFG_TAG只能写1。
ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
}
//启动广播,该函数所用的模式必须和广播初始化中设置的广播模式一样
static void advertising_start(bool erase_bonds)
{
if(erase_bonds == true)
{
//删除flash中存储的配对信息执行完删除后会产生PM_EVT_PEERS_DELETE_SUCCEEDED事件
//在该事件下会启动广播
delete_bonds();
}
else
{
//使用广播初始化中设置的广播模式启动广播
ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
//检查函数返回的错误代码
APP_ERROR_CHECK(err_code);
}
}
//排队写入事件处理函数,用于处理排队写入模块的错误
static void nrf_qwr_error_handler(uint32_t nrf_error)
{
//检查错误代码
APP_ERROR_HANDLER(nrf_error);
}
//服务初始化,包含初始化排队写入模块和初始化应用程序使用的服务
static void services_init(void)
{
ret_code_t err_code;
//定义排队写入初始化结构体变量
nrf_ble_qwr_init_t qwr_init = {0};
//排队写入事件处理函数
qwr_init.error_handler = nrf_qwr_error_handler;
//初始化排队写入模块
err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
APP_ERROR_CHECK(err_code);
//串口透传服务初始化
service_nus_init();
}
#if 1
//配对管理器事件处理函数
static void pm_evt_handler(pm_evt_t const * p_evt)
{
//1、打印日志 2、连接已绑定设备时启动加密 3、出现错误调用错误处理程序
pm_handler_on_pm_evt(p_evt);
//清理配对设备在flash中的保存的绑定信息当flash存储空间不足时删除
//排列最低的配对设备的信息
pm_handler_flash_clean(p_evt);
switch(p_evt->evt_id)
{
//存储的绑定信息已成功删除
case PM_EVT_PEERS_DELETE_SUCCEEDED:
//若程序启动时执行了删除绑定信息操作,在该事件下启动广播
advertising_start(false);
break;
case PM_EVT_CONN_SEC_CONFIG_REQ:
{
// 拒绝来自已经绑定对等方的配对请求.
pm_conn_sec_config_t conn_sec_config = {.allow_repairing = true};
pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
} break;
default:
break;
}
}
#endif
//配对管理器初始化
static void peer_manager_init(void)
{
ble_gap_sec_params_t sec_param;
ret_code_t err_code;
//初始化配对管理器软件库
err_code = pm_init();
APP_ERROR_CHECK(err_code);
//配置安全参数前先清零结构体sec_param
memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));
//初始化安全性参数结构体,
sec_param.bond = SEC_PARAM_BOND; //支持绑定
sec_param.mitm = SEC_PARAM_MITM; //无MITM保护
sec_param.lesc = SEC_PARAM_LESC; //不支持安全连接配对,即使用传统配对
sec_param.keypress = SEC_PARAM_KEYPRESS; //无按键通知
sec_param.io_caps = SEC_PARAM_IO_CAPABILITIES; //无IO能力
sec_param.oob = SEC_PARAM_OOB; //不支持OOB
sec_param.min_key_size = SEC_PARAM_MIN_KEY_SIZE; //最小加密密钥大小7字节
sec_param.max_key_size = SEC_PARAM_MAX_KEY_SIZE; //最大加密密钥大小16字节
//本地密钥分发配置
sec_param.kdist_own.enc = 1; //分发本地LTK
sec_param.kdist_own.id = 1; //分发本地IRK
//对端密钥分发配置
sec_param.kdist_peer.enc = 1; //要求对方分发LTK
sec_param.kdist_peer.id = 1; //要求对方分发IRK
//配置安全参数
err_code = pm_sec_params_set(&sec_param);
APP_ERROR_CHECK(err_code);
//向配对管理器注册事件句柄
err_code = pm_register(pm_evt_handler);
APP_ERROR_CHECK(err_code);
}
//FDS事件处理函数
static void fds_evt_handler(fds_evt_t const * p_evt)
{
//判断事件类型
switch (p_evt->id)
{
case FDS_EVT_INIT://FDS初始化事件
if (p_evt->result == NRF_SUCCESS)//初始化成功
{
my_fds_info.busy = false;
}
break;
case FDS_EVT_WRITE://FDS写记录事件
{
if (p_evt->result == NRF_SUCCESS)//写记录成功
{
my_fds_info.busy = false;
}
} break;
case FDS_EVT_UPDATE://FDS更新记录事件
{
if (p_evt->result == NRF_SUCCESS)//写记录成功
{
my_fds_info.busy = false;
}
} break;
case FDS_EVT_GC://FDS碎片整理事件
{
if (p_evt->result == NRF_SUCCESS)//碎片整理成功
{
my_fds_info.busy = false;
}
} break;
default:
break;
}
}
//等待FDS初始化完成
static void wait_for_fds_ready(void)
{
while (my_fds_info.busy)
{
(void) sd_app_evt_wait();
}
}
// 读取方案并且发送
void read_scheme(void)
{
ret_code_t rc;
//定义并初始化记录描述符结构体变量
fds_record_desc_t desc = {0};
//定义并初始化记录查找令牌结构体变量
fds_find_token_t tok = {0};
uint16_t tempidvalue;
//清零tok从头查找
memset(&tok, 0x00, sizeof(fds_find_token_t));
//在DEVICE_FILE文件中查找记录m_version_record
rc = fds_record_find(DEVICE_FILE, DEVICE_SCHEME_KEY, &desc, &tok);
//查找到记录后,读取记录内容
if(rc == NRF_SUCCESS)
{
fds_flash_record_t temp = {0};
//打开记录读取记录内容
rc = fds_record_open(&desc, &temp);
APP_ERROR_CHECK(rc);
//拷贝记录内容
memcpy(&SchemeData, temp.p_data, sizeof(SchemeData_t));
//读取后,关闭记录
rc = fds_record_close(&desc);
APP_ERROR_CHECK(rc);
// 连接状态下发送方案ID
if(DeviceConnectState == ConnectState)
{
SchemeQuery(SchemeData.SchemeIDMSB,SchemeData.SchemeIDLSB);
}
// 未连接状态下给默认的治疗方案
else
{
StimStateInfoStructInit(SchemeData);
}
}
}
// 存储管理中断处理函数
void FdsHandler(fds_record_desc_t *Desc,fds_find_token_t *Tok)
{
ret_code_t rc;
if(my_fds_info.read == true)//读取记录
{
my_fds_info.read = false;
read_scheme();//读取记录数据,并发送
}
//更新记录m_fw_record
if((my_fds_info.scheme_update == true) && (my_fds_info.busy == false))
{
//清零tok从头查找
memset(Tok, 0x00, sizeof(fds_find_token_t));
//在DEVICE_FILE文件中查找记录m_fw_record
rc = fds_record_find(DEVICE_FILE, DEVICE_SCHEME_KEY, Desc, Tok);
if (rc == NRF_SUCCESS)
{
my_fds_info.busy = true;
my_fds_info.scheme_update = false;
//更新记录m_fw_record
rc = fds_record_update(Desc, &m_SchemePara);
APP_ERROR_CHECK(rc);
wait_for_fds_ready();
NRF_LOG_INFO("fds_record_update");
}
}
}
void wdt_event_handler(void)
{
}
void WdtInit(void)
{
ret_code_t err_code = NRF_SUCCESS;
//定义WDT配置结构体并使用
nrfx_wdt_config_t config = NRFX_WDT_DEAFULT_CONFIG;
//初始化WDT
err_code = nrfx_wdt_init(&config, wdt_event_handler);
//申请喂狗通道,也就是使用哪个
err_code = nrfx_wdt_channel_alloc(&m_channel_id);
APP_ERROR_CHECK(err_code);
//启动WDT
nrfx_wdt_enable();
}
//喂狗函数
void WdtFeed(void)
{
//喂狗
nrfx_wdt_channel_feed(m_channel_id);
}
void clocks_start(void)
{
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;//清零高频时钟启动事件
NRF_CLOCK->TASKS_HFCLKSTART = 1; //启动高频时钟
//等待高频时钟启动完成
while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
}
//驱动初始化
void DeviceInit(void)
{
/* 初始化控制gpio */
GpioInit();
clocks_start();
EXIT_KEY_Init();
/* 初始化APP定时器 */
AppTimersInit();
timer3_rms_init();
/* DACPWM初始化 */
// PwmDACInit();
/* SAADC初始化 */
battery_adc_init();
rms_saadc_init();
/* 采样的PPI初始化 */
//PPIPwmInit();
PPIEegAdcInit();
/* GPIOTE初始化 */
GpioteInit();
// nrf_gpio_pin_clear(H_CTL1_PWM);
// nrf_gpio_pin_clear(H_CTL2_PWM);
/* 刺激PWM初始化 */
pwm0_common_init();
pwm2common_init();
//timer1_output_ctrl_init();
/* 喂狗初始化 */
WdtInit();
}
//广播中添加MAC地址
void MacSet(void)
{
ble_gap_addr_t addr;
uint32_t err_code = sd_ble_gap_addr_get(&addr);
APP_ERROR_CHECK(err_code);
memcpy(BLE_MAC,addr.addr,BLE_GAP_ADDR_LEN);
err_code = sd_ble_gap_addr_set(&addr);
APP_ERROR_CHECK(err_code);
}
void StartAdv(void)
{
//启动广播
advertising_start(false);
}
// 关闭广播
void StopAdv(void)
{
NRF_LOG_INFO("StopAdv!");
sd_ble_gap_adv_stop(m_advertising.adv_handle);
}
void user_ble_init(void)
{
//初始化协议栈
ble_stack_init();
//变量初始化
VariableInit();
//配置GAP参数
gap_params_init();
//初始化GATT
gatt_init();
//初始化服务
services_init();
//在广播数据中添加MAC地址
MacSet();
//初始化广播
advertising_init();
//连接参数协商初始化
conn_params_init();
//配对管理器初始化
peer_manager_init();
//LOG打印信息
NRF_LOG_INFO("BLE started.");
}
//fds存储管理初始化
void fdsInit(ret_code_t rec,fds_record_desc_t desct,fds_find_token_t token)
{
//注册FDS事件回调函数接收FS事件
(void)fds_register(fds_evt_handler);
my_fds_info.busy = true;
rec = fds_init();//初始化FDS
APP_ERROR_CHECK(rec);//用错误处理模块检查函数返回值
//FDS初始化是异步的因此要等待FDS初始化完成
wait_for_fds_ready();
//清零tok从头查找
memset(&token, 0x00, sizeof(fds_find_token_t));
//在DEVICE_FILE文件中查找记录m_desp_record
rec = fds_record_find(DEVICE_FILE, DEVICE_SCHEME_KEY, &desct, &token);
NRF_LOG_INFO("rec = %d",rec);
//没有查找到m_desp_record记录写入记录
if (rec != NRF_SUCCESS)
{
my_fds_info.busy = true;
StimStateInfoStructInit(PreStorageSchemeData); // 用预存的信息给刺激参数赋值
memcpy(SchemePara.text,&PreStorageSchemeData,sizeof(PreStorageSchemeData));
rec = fds_record_write(&desct, &m_SchemePara);
APP_ERROR_CHECK(rec);
wait_for_fds_ready();
}
else
{
my_fds_info.read = true;
}
}
//主函数
int main(void)
{
ret_code_t rc;
//定义并初始化记录描述符结构体变量
fds_record_desc_t desc = {0};
//定义并初始化记录查找令牌结构体变量
fds_find_token_t tok = {0};
//初始化log程序模块
log_init();
//滤波器初始化
FilterInit();
// 设备预存信息初始化ID :101 腹直肌分离
PreStorageSchemeDataInit();
//fds存储管理初始化
fdsInit(rc,desc,tok);
//器件初始化
DeviceInit();
//初始化电源管理
power_management_init();
user_ble_init();
//启动已经创建的APP定时器
ApplicationTimersStart();
//主循环
while(true)
{
// 存储事件处理函数
FdsHandler(&desc,&tok);
//处理挂起的LOG和运行电源管理
idle_state_handle();
WdtFeed();
if(POWER_CLOSE == DeviceState)
{
continue;
}
KeyPinHandler();// 按键中断处理
//输出电流控制
/* 适配器和电极片脱落状态变化上传 */
if(AdapterState == LastAdapterState)
{
StateUpLoad(AdapterState,ElectrodeStatusInfo);
if(AdapterState == AdapterConnected)
{
LastAdapterState = AdapterNotConnected;
}
else
{
LastAdapterState = AdapterConnected;
}
}
if(ElectrodeStatusInfo == LastElectrodeStatusInfo)
{
StateUpLoad(AdapterState,ElectrodeStatusInfo);
if(ElectrodeStatusInfo == ElectrodeFalloff)
{
LastElectrodeStatusInfo = ElectrodeConnectted;
}
else
{
LastElectrodeStatusInfo = ElectrodeFalloff;
}
}
}
}