-
STM32F103C8T6 -
MAX30100 Heart Rate and Blood Oxygen Detection Module -
MPU6050 Attitude Detection Module -
0.96-inch OLED Display Module
-
Program Outline
-
CubeMX Configuration -
Clock Configuration
-
Clock Source
-
Peripheral Configuration
-
FreeRTOS Settings
Create a task for data collection from MPU6050 and set this task’s priority to high because the fall detection priority should be the highest.
In addition, there are tasks for data processing of MAX30100, abnormal state handling of MPU, and OLED refresh tasks.
Here, I decided to use a global buffer to store and process data (instead of using temporary variables in tasks).
Set a timer to periodically read the MAX30100 code and set three semaphores for notifying MAX30100 data processing, MPU6050 abnormal handling, and OLED refresh display.
Use an event group to mark various states, such as the initialization state of each module and the abnormal state of MPU6050.
-
KEIL Code
In Keil FreeRTOS, it helps us to initialize, but we need to enable the timer ourselves and set the timer interval to 15ms.
float x[3];// store int stepSum;/* USER CODE END Header_MPURuning */void MPURuning(void *argument){ /* USER CODE BEGIN MPURuning */ /* Infinite loop */ for(;;) { MPU6050_ReadAccel(&mpu6050, x, x+1, x+2); // here x[3] is the acceleration in three directions, which should calculate the resultant acceleration //printf("B:%.2f\r\n",x[2]); if(x[1]>1.5&&x[1]!=1.9) { stepNow = stepSum; MpuErrorFlag = 1; xSemaphoreGive(MPU6050EventHandle); } if(x[1]>0.7) { stepSum++; } osDelay(10); } /* USER CODE END MPURuning */}
Here, MPU6050 continuously acquires acceleration and determines whether a fall occurs by adjusting the threshold.
void MAXData(void *argument){ /* USER CODE BEGIN MAXData */ static int i = 0; uint32_t DCRED; update(); DCRED = -removeRedDcComponent(rawIRValue)*2; Data[i++] = DCRED; if(i==300) { xSemaphoreGive(MAXStartHandle); i = 0; } /* USER CODE END MAXData */}
MAX30100 reads data at a sampling rate of 15ms through a software timer, writing the data into a buffer, and sends a binary semaphore when 300 samples are collected.
void MAXUsing(void *argument){ /* USER CODE BEGIN MAXUsing */ /* Infinite loop */ for(;;) { if (xSemaphoreTake(MAXStartHandle,portMAX_DELAY)) { xTimerStop(MAXDataHandleHandle,0); smoothFilter(Data,smooth,BuffLenth,3); int index; float cot; float sum; float Heart;// for(int i = 0;i<300;i++)// {// printf("A:%d\r\n",smooth[i]);// } for(int i = 0;i<300;i++) { if(smooth[i]>70&&smooth[i]<300) { index = i; while(smooth[i]>0) { i++; if(i==BuffLenth-1) { break; } } while(smooth[i]<70) { i++; if(i==BuffLenth-1) { break; } } index = i-index; if(index>23)//23 corresponds to heart rate 173 considered error { sum+= index; cot++; //printf("A:%d\r\n",index); } } } Heart = (float)(sum/cot)*0.015; Heart = 60/Heart; HeartFre= Heart; sum = 0; cot = 0; if((HeartFre>160&&HeartFre<250)||HeartFre<30) { xEventGroupSetBits(TaskStauteHandle,1<<0);// set bit 0 to 1 } xSemaphoreGive(OLEDShowSemHandle); xTimerStart(MAXDataHandleHandle,0); } } /* USER CODE END MAXUsing */}
The MAX30100 data processing task waits for the semaphore to be released. When it receives the semaphore, it stops the timer and processes the data.
Extract heart rate information from the data (blood oxygen information can also be extracted, but calibration is required, which I did not do).
If abnormal heart rate is detected, set the 0th bit of the event group to 1.
Then notify the OLED function to refresh.
void MPUERROR(void *argument){ /* USER CODE BEGIN MPUERROR */ /* Infinite loop */ for(;;) { int flag = 1; if (xSemaphoreTake(MPU6050EventHandle, portMAX_DELAY) == pdPASS) { for(int i = 0;i<200;i++) { //printf("A:%d\r\n",stepSum); if((stepNow-stepSum)>2) { flag = 0; } osDelay(10); } if(flag) { xEventGroupSetBits(TaskStauteHandle,1<<1);// set bit 1 to 1 xSemaphoreGive(OLEDShowSemHandle); } } /* USER CODE END MPUERROR */ }
In the MPU6050 abnormal data handling task, if there is no increase in steps within two seconds (in actual situations, a longer time should be set), it marks an abnormal position and then notifies the OLED to refresh.
void OLEDShow(void *argument){ /* USER CODE BEGIN OLEDShow */ char s[50]; /* Infinite loop */ for(;;) { if (xSemaphoreTake(OLEDShowSemHandle,portMAX_DELAY)) { sprintf(s,"Heart:%.2f",HeartFre); OLED_Clear(); OLED_ShowString(0,1,(unsigned char *)s,2); EventBits_t Bits = xEventGroupGetBits(TaskStauteHandle); if(Bits&1<<0) { // abnormal at bit 0, heart rate abnormal sprintf(s,"Heart is Error"); OLED_ShowString(0,2,(unsigned char *)s,2); } if(Bits&1<<1) { // abnormal at bit 1, posture abnormal sprintf(s,"MPU Error"); OLED_ShowString(0,3,(unsigned char *)s,2); } } } /* USER CODE END OLEDShow */}
In the OLED refresh task, it displays the heart rate in real-time and shows error messages based on the errors.
Effect Display
Due to the rushed nature of the project, it is demonstrated using Dupont wires and the core board.