/*@ 20/05/26

 * - CUBEMX 설정 Active, Passive 모드 두 가지 상황으로 분리

 * - [동작] 부분 액티브, 패시브 모드에서의 동작 상황 설명 추가

 * - 액티브 모드 코드 및 설명 재수정 (타이머 인터럽트 제거)

 * - 패시브 모드 코드 및 설명 수정 (타이머 인터럽트 추가)

 *

 *@ 20/05/21 

 * - CUBE MX 타이머 인터럽트 설정 추가

 * - 타이머 인터럽트를 사용한 PMS7003 데이터 수신으로 변경

 * - 수신 완료 콜백 함수 코드 수정

 * - 타이머 부분 추가

 * - 액티브 모드 부분 코드 및 설명 수정

 * - 코드 링크 추가

 */

 

PMS7003 먼지센서

  • VCC : 5V (4.5~5.5V 내부의 팬이 5V로 동작되어야 하기 때문)
  • 통신 방식 : UART
  • DATA : 3.3V (MCU가 5V로 작동한다면 R/X, T/X핀에 Level Shifter 연결 필요 (5V->3.3V))
  • 인터페이스 레벨 : L<0.8, H>2.7 (3.3V)
  • 측정 범위 : 0.3~1.0 / 1.0~2.5 / 2.5~10 (㎛)
  • 검출 효율 : 50% (0.3㎛), 98%(>=0.5㎛)
  • 유효 범위 (pm2.5 기준) : 0~500 ㎍/㎥
  • 최대 범위 (pm2.5 기준) : >=1000 ㎍/㎥
  • 해상도 : 1 ㎍/㎥

[하드웨어 연결]

핀 설정
어댑터 보드
1.27mm / 2.00mm / 2.54mm 보드

주의점

  • SET, RESET 핀은 내부 풀업 되있으므로 사용하지 않는다면 연결해선 안된다
  • PIN7, 8은 연결해선 안된다

연결

  • 손쉬운 연결을 위해 PMS7003 어댑터 보드, 1.27mm-2.00mm-2.54mm 보드 및 1.25mm 커넥터를 사용
  • UART TX -> PMS7003 RX, UART RX -> PMS7003 TX 에 연결
  • 데이터 라인의 전압이 3.3V을 필요로 하므로 5V로 작동하는 MCU를 사용할 경우, TX, RX 라인에 Level Shifter 를 사용해 데이터 라인의 전압을 변환시킬 필요가 있다
  • ex) MCU UART TX(5V) -> 5V to 3.3V Level Shifter -> PMS7003 RX(3.3V)
  •     PMS7003 TX(3.3V) -> 3.3V to 5V Level Shifter -> MCU UART RX

[CUBE MX 설정]

- Active, Passive 모드에 따라 UART DMA, Timer 다르게 설정

 

<공통>

- UART 설정

  • 보드 레이트 : 9600 bps
  • 데이터 길이 : 8 비트
  • 패리티 비트 : X
  • 스탑 비트 : 1
  • 인터럽트 활성화

UART 설정
인터럽트 설정

<Active Mode>

- UART DMA - Rx Circular 모드로 설정

- 타이머 인터럽트 사용 X

UART DMA Rx - Circular 모드로 설정 (Tx - Normal)

<Passive Mode>

- DMA 미사용, 인터럽트를 통해 송수신 (DMA 사용 설정 되있어도 상관 X)

타이머 인터럽트 설정

 

- 타이머 인터럽트 설정

  • 타이머 주기 : 해당 타이머 인터럽트가 속하는 APBx 타이머 클럭 속도 / (Prescaler + 1) + (Counter Period + 1)
  • WB55 : TIM16 - APB2 (Timer Clock=32Mhz)
  •           32,000,000 / {((32,000 -1) + 1)) + ((5,000 - 1) + 1)} = 0.2 Hz = 5 Sec (T=1/f)
  • 5초 주기로 타이머 인터럽트 작동

[동작]

- 두개의 디지털 출력 옵션 : 액티브(디폴트 설정), 패시브 모드

- 액티브 모드 : PMS7003 센서는 지속적으로 측정한 센서값을 호스트로 자동 전송하고 호스트는 DMA Rx를 Circular 모드로 설정함으로써 PMS7003 데이터 수신 마지막에 자동으로 transfer count를 리셋하고 소스 버퍼의 첫번째 바이트부터 다시 수신을 시작한다

- 패시브 모드 : PMS7003에 Change Mode 명령어+패시브 모드 명령어를 전송해 PMS7003을 패시브 모드로 변경하고 데이터를 읽어올 땐 Passive Read 명령어를 보낸 뒤 UART 수신 명령어를 사용해 센서 데이터를 수신한다

- 커맨드 명령어를 통해 슬립, 웨이크업 기능을 사용할 수 있다

- 팬 성능으로 인해 센서가 슬립 모드에서 깨어나고 적어도 30초 지난 뒤에야 안정된 데이터를 얻는게 가능

 

<액티브 모드>

  • 액티브 모드에서 센서는 자동으로 시리얼 데이터를 호스트에 보냄
  • 액티브 모드는 두개의 서브 모드로 분리 : Stable 모드, Fast 모드
  • 농도 변화가 작으면 센서는 2.3s 간격의 스테이블 모드로 작동
  • 농도 변화가 크다면 센서는 200~800ms 간격의 패스트 모드로 자동 변경 (농도가 높을 수록 더 짧은 간격을 가짐)
  • 호스트(MCU)로 전송하는 데이터의 길이 : 32Bytes (각 8bit의 크기를 가진 32개의 배열)

(00~31 중 사용할 부분만 정리)

00 : 스타트 캐릭터1 : 0x42 고정 

01 : 스타트 캐릭터2 : 0x4d 고정

(고정값이므로 UART 통신이 문제없이 이뤄지고 있는지 확인하는데 사용할 수 있다)

 

10 : 데이터4 상위 8비트 : PM1.0에서의 먼지 농도 ㎍/㎥(대기 환경 하)

11 : 데이터4 하위 8비트

12 : 데이터5 상위 8비트 : PM2.5에서의 먼지 농도 ㎍/㎥(대기 환경 하)

13 : 데이터5 하위 8비트

14 : 데이터6 상위 8비트 : PM10에서의 먼지 농도 ㎍/㎥(대기 환경 하)

15 : 데이터6 하위 8비트

 

16 : 데이터7 상위 8비트 : 0.1L의 공기 중 지름 0.3um 이상의 입자의 갯수를 나타냄

17 : 데이터7 하위 8비트

18 : 데이터8 상위 8비트 : 0.1L의 공기 중 지름 0.5um 이상의 입자의 갯수를 나타냄

19 : 데이터8 하위 8비트

20 : 데이터9 상위 8비트 : 0.1L의 공기 중 지름 1.0um 이상의 입자의 갯수를 나타냄

21 : 데이터9 하위 8비트

22 : 데이터10 상위 8비트 : 0.1L의 공기 중 지름 2.5um 이상의 입자의 갯수를 나타냄

23 : 데이터10 하위 8비트

24 : 데이터11 상위 8비트 : 0.1L 공기 중 지름 5.0um 이상을 갖는 입자의 갯수를 나타냄

25 : 데이터11 하위 8비트

26 : 데이터12 상위 8비트 : 0.1L 공기 중 지름 10um 이상을 갖는 입자의 갯수를 나타냄

27 : 데이터12 하위 8비트

 

30 : 데이터와 체크 상위 8비트 : check code = 스타트 캐릭터1+스타트 캐릭터2+...+데이터13 하위 8비트

31 : 데이터와 체크 하위 8비트 

 

*데이터1~데이터3의 먼지 농도는 공장 환경 하에서 사용이 권장되기에 제외

 

<패시브 모드>

  • 디폴트 보드 레이트 : 9600bps, 체크 비트 : none, 스탑 비트 : 1 비트
  • 각 8비트(1바이트)의 값을 가지는 총 7개의 배열을 전송해야 한다
  • 1,2번 배열의 값은 고정인 상태로 명령어(CMD)와 그 명령어에 따른 필요 데이터를 데이터1,2 배열에 입력하고 Verify Byte 1,2에 Start Byte1~Data2 까지의 배열의 합을 상위, 하위 각 8비트 나누어 저장한 뒤 센서에 전송
  • 패시브 모드에서 센서로부터 읽어오는 데이터의 크기와 내용은 액티브 모드와 동일하다

스타트 바이트 1 : 0x42 (고정)

스타트 바이트 2 : 0x4d (고정)

명령어 : CMD

데이터1 : 데이터 H

데이터2 : 데이터 L

확인 바이트1 : LRC H 확인 바이트를 제외한 모든 바이트 합 (상위8비트)

확인 바이트2 : LRC L (하위 8비트)

 

명령어

0xe2 : 패시브 모드에서 읽기, 32바이트 응답

0xe1 : 00 - Passive 모드로 변경

       : 01 - Active 모드로 변경

0xe4 : 00 - Sleep 설정

       : 01 - Wakeup 설정

 

[코드]

<Active, Passive 모드 공통>

- 전역변수

/* USER CODE BEGIN PV */
uint8_t pms7003_Buffer[32];	//PMS7003 센서로부터 받아 올 32바이트 데이터를 저장하는 배열
uint8_t pms7003_send_buffer[7]={0x42,0x4d,0};	//센서에 명령어를 보내는데 사용될 데이터 배열
/* USER CODE END PV */
  • 측정값을 수신할 데이터 배열, 명령어를 전송할 배열 및 사용에 필요한 변수 선언

- 센서로부터 읽어 온 데이터를 출력하는 함수

void print_PMS7003(void)
{
	/*
	combine_value : 8비트씩 나눠진 데이터를 하나로 합치는데 사용할 변수
	check_byte_receive : 센서로부터 수신 받은 체크 코드(30,31)를 하나로 합치는데 사용할 변수
	check_byte_calculate : 센서로부터 수신 받은 0~29 까지의 데이터를 합산해 체크 코드와 비교
	*/
	uint16_t combine_value, check_byte_receive, check_byte_calculate=0;
    
	check_byte_receive=pms7003_Buffer[30]<<8|pms7003_Buffer[31];
	for(uint8_t i=0;i<30;i++)
	{
		check_byte_calculate+=pms7003_Buffer[i];
	}
    
    	//센서로부터 수신 받은 체크 코드(30,31)와 수신 받은 0~29까지의 데이터 합산이 일치할 때만 출력
	if(check_byte_receive==check_byte_calculate)
	{
		printf("PM1.0 : %d	",(combine_value=(pms7003_Buffer[10]<<8)|pms7003_Buffer[11]));
		printf("PM2.5 : %d	",(combine_value=(pms7003_Buffer[12]<<8)|pms7003_Buffer[13]));
		printf("PM10 : %d	",(combine_value=(pms7003_Buffer[14]<<8)|pms7003_Buffer[15]));
		printf("0.3um : %d	",(combine_value=(pms7003_Buffer[16]<<8)|pms7003_Buffer[17]));
		printf("0.5um : %d	",(combine_value=(pms7003_Buffer[18]<<8)|pms7003_Buffer[19]));
		printf("1.0um : %d	",(combine_value=(pms7003_Buffer[20]<<8)|pms7003_Buffer[21]));
		printf("2.5um : %d	",(combine_value=(pms7003_Buffer[22]<<8)|pms7003_Buffer[23]));
		printf("5.0um : %d	",(combine_value=(pms7003_Buffer[24]<<8)|pms7003_Buffer[25]));
		printf("10.0um : %d\n",(combine_value=(pms7003_Buffer[26]<<8)|pms7003_Buffer[27]));
	}
	else	//체크 코드가 일치하지 않는 경우
	{
	}
}
  • 상, 하나로 나뉘어 저장된 데이터(각 8bit)를 하나로 합친 뒤(16bit) 출력

- 명령어 전송 함수

void write_PMS7003(char* cmd)
{
	//1~5번째까지의 배열의 합을 필요로 하는 6, 7번째 배열인 verify byte1, 2에 값을 저장하기 위해 사용할 변수
	uint16_t verify_byte=0;	

	//사용자가 입력한 명령어(문자열)을 비교하여 해당하는 명령어에 따라
	//센서로 전송할 데이터 배열을 채운다
	if(strcmp(cmd,"Read")==0)
	{
		pms7003_send_buffer[2]=0xe2;
		pms7003_send_buffer[3]=0x00;
		pms7003_send_buffer[4]=0x00;
	}
	else if(strcmp(cmd,"Passive")==0)
	{
		pms7003_send_buffer[2]=0xe1;
		pms7003_send_buffer[3]=0x00;
		pms7003_send_buffer[4]=0x00;
	}
	else if(strcmp(cmd,"Active")==0)
	{
		pms7003_send_buffer[2]=0xe1;
		pms7003_send_buffer[3]=0x00;
		pms7003_send_buffer[4]=0x01;

	}
	else if(strcmp(cmd,"Sleep")==0)
	{
		pms7003_send_buffer[2]=0xe4;
		pms7003_send_buffer[3]=0x00;
		pms7003_send_buffer[4]=0x00;
	}
	else if(strcmp(cmd,"WakeUp")==0)
	{
		pms7003_send_buffer[2]=0xe4;
		pms7003_send_buffer[3]=0x00;
		pms7003_send_buffer[4]=0x01;
	}
	for(uint8_t i=0;i<5;i++)
	{
		verify_byte+=pms7003_send_buffer[i];	//verify byte1,2=배열 1~5(buffer[0]~[4])까지의 합
	}
	pms7003_send_buffer[5]=verify_byte>>8;	//배열 1~5까지의 합을 상하로 나누어 저장
	pms7003_send_buffer[6]=verify_byte;

	//센서와의 통신에 사용할 UART 라인이 준비 상태가 될 때까지 대기
	while(HAL_UART_GetState(&hlpuart1)!=HAL_UART_STATE_READY)
	{

	}
	//인터럽트를 이용해 센서에 명령어 전송
	if(HAL_UART_Transmit_IT(&hlpuart1,(uint8_t*)pms7003_send_buffer,7)!=HAL_OK)
	{

	}

	//만약 사용자가 입력한 명령어가 Read라면 센서로부터 측정 데이터를 받아오는 함수를 실행한다
	if(strcmp(cmd,"Read")==0)
	{
		//UART가 준비 상태가 될 때까지 대기
		while(HAL_UART_GetState(&hlpuart1)!=HAL_UART_STATE_READY)
		{

		}
		//인터럽트를 통해 센서로부터 데이터 수신
		if(HAL_UART_Receive_IT(&hlpuart1,pms7003_Buffer, 32)!=HAL_OK)
		{

		}
	}
}
  • 센서에 명령을 내리기 위한 데이터 배열을 전송하는 함수
  • 1,2번째 배열은 각 0x42, 0x4d로 고정값을 갖고 6,7번째 배열엔 1~4번째 배열의 합이 상,하 8비트로 나뉘어 저장되어야 한다
  • 명령어가 "Read"일 경우, 마지막에 인터럽트를 이용한 UART 수신을 통해 센서로부터 데이터를 받아온다 (패시브 모드에서 수신하는 데이터도 액티브 모드와 동일한 데이터 순서, 길이를 갖는다)

<Active Mode>

- 메인 함수

int main(void)
{
  ...
  /* USER CODE BEGIN 2 */

  if(HAL_UART_Receive_DMA(&hlpuart1,pms7003_Buffer,32)!=HAL_OK)
  {
	  //Execution fail
  }

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
  • DMA를 통한 UART 수신 함수 실행
  • UART DMA - Rx를 Circular 모드로 설정했음으로 센서가 데이터를 전송할 때마다 수신이 자동 시작된다

- 수신 완료 후 호출되는 UART 콜백 함수

/* USER CODE BEGIN 4 */

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance==LPUART1)
	{
		print_PMS7003();
	}
}
  • USER CODE 4 부분은 main.c 내 하단에 위치
  • UART 데이터 수신이 완료되면 HAL_UART_RxCpltCallback() 함수가 자동으로 호출된다
  • 콜백 함수는 자동 생성되지 않으므로 main.c 내에 직접 작성할 필요가 있다

- 결과

사용 코드 : https://github.com/lee35210/STM32WB_PMS7003/tree/active

 

<Passive Mode>

- 타이머 초기화 함수

static void MX_TIM16_Init(void)
{

  /* USER CODE BEGIN TIM16_Init 0 */

  /* USER CODE END TIM16_Init 0 */

  /* USER CODE BEGIN TIM16_Init 1 */

  /* USER CODE END TIM16_Init 1 */
  htim16.Instance = TIM16;
  htim16.Init.Prescaler = 32000-1;
  htim16.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim16.Init.Period = 5000-1;
  htim16.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim16.Init.RepetitionCounter = 0;
  htim16.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim16) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM16_Init 2 */

  HAL_TIM_Base_Start_IT(&htim16);

  /* USER CODE END TIM16_Init 2 */

}
  • CUBEMX main.c 하단에 자동 생성되는 타이머 초기화 함수
  • 함수 내 하단 사용자 코드란에 HAL_TIM_Base_Start_IT(&타이머 이름) 을 입력해야 타이머 인터럽트가 시작된다

- 메인 함수

int main(void)
{
	...
  /* USER CODE BEGIN 2 */
  
  //PMS7003 Passive Mode로 설정
  write_PMS7003("Passive");

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */

/**
  * @brief  Tx Transfer completed callback
  * @param  huart: UART handle.
  * @note   This example shows a simple way to report end of DMA Tx transfer, and
  *         you can add your own implementation.
  * @retval None
  */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance==LPUART1)
	{

	}
}

/**
  * @brief  Rx Transfer completed callback
  * @param  UartHandle: UART handle
  * @note   This example shows a simple way to report end of IT Rx transfer, and
  *         you can add your own implementation.
  * @retval None
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance==LPUART1)
	{
		print_PMS7003();
	}
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM16)
	{
		write_PMS7003("Read");
	}
}
  • 설정한 타이머 주기마다 HAL_TIM_PeriodElapsedCallback() 함수가 호출되고 PMS7003에 Read 명령어를 전송한다
  • 각종 명령어를 전송한 직후에 HAL_UART_TxCpltCallback() 함수가 호출된다
  • PMS7003으로부터 데이터 수신 완료 후, HAL_UART_RxCpltCallback() 함수가 호출되고 수신한 데이터를 출력한다

- 결과

출력 결과

사용 코드 : https://github.com/lee35210/STM32WB_PMS7003/tree/passive

 

<Sleep, Wakeup>

  • 슬립 모드에선 팬의 작동이 정지되고 센서로부터의 데이터 수신이 불가능해진다
  • 예시
int main(void)
{
	...
    /* USER CODE BEGIN 2 */
  uint8_t sleep_mode=0; (슬립 모드 확인하는데 사용할 변수, 1=슬립 모드 상태)
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  if((receive_complete==0)&&(sleep_mode==0))	//슬립 모드 상태일 때는 데이터 수신 실행 X
	  {
		  while(HAL_UART_GetState(&hlpuart1)!=HAL_UART_STATE_READY)
		  {
		  }
		  HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)pms7003_Buffer,32);
	  }
	  if(receive_complete==1)
	  {
		  print_PMS7003();
	  }

	  if(30<i&&i<40)
	  {
		  if(sleep_mode==0)
		  {
			  printf("Sleep\r\n");
			  write_PMS7003("Sleep");
			  sleep_mode=1;
		  }
	  }
	  else if(40<=i)
	  {
		  printf("WakeUp\r\n");
		  write_PMS7003("WakeUp");
		  sleep_mode=0;
		  i=0;
	  }
	  i++;
	  HAL_Delay(1000);
  }
  /* USER CODE END 3 */
}
  • 주기적으로 슬립, 웨이크업 반복

출력 결과

  • 슬립 모드 직후, 센서로부터 받아 온 데이터에는 비어있는 부분들이 존재하지만 센서 작동 시간이 늘어감에 따라 측정값들이 채워지고 안정화되어 간다
  • STM32WB55 SoC에서 BLE, Thread, MAC 802.15.4 를 사용하기 위해선 M0+ CPU에 사용할 통신 방법에 맞는 Wireless Stack 펌웨어가 업로드 되어 있어야 한다 (Nordic nRF 칩셋에서 SoftDevice 같은 역할)
  • 펌웨어 업로드 방법 및 펌웨어에 대한 설명은 STM32Cube의 Repository 폴더내에 저장되어있다
  • 기본 설치 위치
  • C:/Users/사용자명/STM32Cube/Repository/STM32Cube_FW_WB_V1.2.0/Projects/STM32WB_Copro_Wireless_Binaries/Release_Notes.html
  • STM32Cube_FW_WB_V1.2.0 - 펌웨어 버전에 따라 V.1.2.0이 아닌 다른 표기가 올 수 있음 (19/10/16 기준 1.2.0)

[펌웨어 업로드 과정]

  • STM32Cube\Repository\STM32Cube_FW_WB_V1.2.0\Projects\P-NUCLEO-WB55.Nucleo\Applications\BLE\BLE_Proximity 예제 사용을 전제로 이에 필요한 stm32wb5x_BLE_Stack_fw.bin 펌웨어를 업로드하는 과정

1. STM32CubeProgrammer 프로그램 설치

 - https://www.st.com/content/st_com/en/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-programmers/stm32cubeprog.html

 

STM32CubeProg - STMicroelectronics

STM32CubeProg - STM32CubeProgrammer software for programming STM32 products, STM32CubeProg, STMicroelectronics

www.st.com

2. Command Line Interface (CLI) mode 사용하기 위한 설정

(명령 프롬프트에서 STM32_Programmer_CLI.exe 사용하기 위한 과정)

- STM32CubeProgrammer 기본 설치 위치

C:\Program Files\STMicroelectronics\STM32Cube\STM32CubeProgrammer\

- STM32CubeProgrammer 폴더 내에 STM32_Programmer_CLI.exe 가 위치해있는 bin 폴더를 추가

 

3. 부트로더 USB 인터페이스 액세스

* Nucleo 보드

- Boot0(CN7.7) 핀을 VDD(CN7.5)에 연결하는 것으로 부트모드 설정

- BOOT0과 VDD 핀을 점퍼 케이블 등을 이용해 연결

- JP1 5V soruces의 점퍼핀을 USB MCU에 꽂고 컴퓨터와 연결된 USB 케이블을 USB_USER 커넥터에 연결

* USB-Dongle

- 스위치를 BOOT0 방향으로 이동

 

4. 현재 M0+ CPU에 저장되어 있는 Wireless Stack 펌웨어 삭제

- 명령 프롬프트 or 윈도우 파워쉘 실행

 

* 명령 프롬프트 실행

- 시작버튼 우클릭 > 실행 (or 윈도우 펑션키 + r)

명령 프롬프트 실행
명령 프롬프트 실행 결과

- CD 명령어 + 무선 펌웨어가 저장되어 있는 폴더 경로를 복사 붙여넣기한 후 실행해 폴더 위치 이동


* 윈도우 파워쉘 실행

- Wireless 펌웨어가 저장되어있는 폴더로 이동 (STM32Cube\Repository\STM32Cube_FW_WB_V1.2.0\Projects\STM32WB_Copro_Wireless_Binaries)

- 좌측 상단 파일 > Windows PowerShell 열기 > Windows PowerShell 열기 클릭

펌웨어 폴더로 이동
윈도우 파워쉘 실행
파워쉘 실행 결과

- STM32_Programmer_CLI.exe -c port=usb1 -fwdelete 명령어 실행해 현재 저장되어있는 펌웨어 삭제


5. FUS(Firmware Upgrade Service) 버전 읽기, 업그레이드

- STM32_Programmer_CLI.exe -c port=usb1 -r32 0x20030030 1

0x20030030 : 00050300 : FUSv0.5.3 => 새로운 FUS 펌웨어 업그레이드 필요 

0x20030030 : 01000100 or 01000200 : FUSv1.0.x => 업데이트 된 상태. 바로 M0+ CPU에 Wireless 펌웨어 다운 가능

 

5-2. FUS 업그레이드

- STM32_Programmer_CLI.exe -c port=usb1 -fwupgrade [FUS_Binary] [Install@] firstinstall=0

- STM32_Programmer_CLI.exe -c port=usb1 -fwupgrade stm32wb5x_FUS_fw.bin 0x080EC000 firstinstall=0

- [FUS_Binary] : FUS 펌웨어 파일명 (확장자 포함)

- [Install@] : 인스톨 주소

- Release_Notes.html 하단에 나와있는 표를 참고로 인스톨 주소 입력 (SoC 플래시 메모리 용량에 따라 달라진다)

(P-NUCLEO-WB55 개발킷의 경우 누클레오 보드, 동글 둘 다 플래시 메모리가 1MB 이므로  0x080EC000 사용)

*Error : Firmware not authentic! 에러가 발생하는 경우

- 우선적으로 STM32_Programmer_CLI.exe -c port=usb1 -r32 0x20030030 1 명령어 사용한 뒤 결과를 확인해 업그레이드가 됐는지 안됐는지를 확인

- 안됐을 경우 아래 방법 시도

https://community.st.com/s/question/0D50X0000Ap44FlSQI/restore-stm32wb55-fus-firmware

 

ST Community

 

community.st.com

- 위의 링크 마지막 방법으로 해결 (보드 연결 방식 ST-LINK가 아닌 USB)

  • STM32Programmer 실행 후, 왼쪽 상단 세 줄 아이콘 클릭 후 Erasing & programming 메뉴 선택

  • 우측 상단에서 USB 선택 후 Connect 버튼 클릭

  • 체크 박스 해제 후, 재클릭으로 전체 섹터 선택한 다음 Erase selected sectors 버튼 클릭하여 선택된 섹터 삭제 시작

  • 뒷부분에 보호된 섹터를 제외한 나머지 섹터가 지워진다
  • 이후 5-2. FUS 업그레이드 과정 재시도

섹터 지우기 후, FUS 확인 결과

  • FUS 재업그레이드 과정 중 에러가 발생했지만 FUS가 제대로 업그레이드 된 것을 확인 가능

6. Wireless Stack 펌웨어 M0+ CPU에 다운로드

- STM32_Programmer_CLI.exe -c port=usb1 -fwupgrade [Wireless_Coprocessor_Binary] [Install@] firstinstall=1

- STM32_Programmer_CLI.exe -c port=usb1 -fwupgrade stm32wb5x_BLE_Stack_fw.bin 0x080CC000 firstinstall=1

- [Wireless_Coprocessor_Binary] : M0+ CPU에 다운로드 할 Wireless Stack 펌웨어

- 필요에 따라 STM32WB_Copro_Wireless_Binaries 폴더 내에 다른 Wireless Stack 펌웨어를 선택하면 된다

- [Install@] : 인스톨 주소 (Wireless 펌웨어에 따라 달라진다. Release_Notes.html 하단 표에 주소 표기되어 있음)

stm32wb5x_BLE_Stack_fw.bin 를 다운로드 하는 과정

7. 노말 모드로 복귀

- BOOT0 핀과 VDD 핀의 연결 해제

- JP1의 점퍼핀을 USB STL에 꽂고 컴퓨터와 연결된 USB 케이블을 ST-LINK 커넥터에 연결

8. BLE_Proximity Example 프로젝트 업로드 후, 확인

- 업로드 이후, 블루투스 목록에 보이지 않는다면 USB 케이블 재연결로 보드 다시 부팅

- ST사에서 제공하는 ST BLE Profile 앱으로 연결 및 작동이 정상적으로 되는지 확인

  • 작동 전압 : 4.5~5.25V
  • 통신 방법 : UART, Modbus
  • 보드 레이트 : 9600bps
  • 측정 범위 : 400~2000ppm (PWM). 0~10000ppm (UART)
  • 정확도 : ±40ppm ±3%
  • 측정 간격 : 4초
  • 소비 전력 : 최대 300mA, 평균 18mA 이하
  • 마스터에서만 트랜잭션 시작 가능 (마스터에서 슬레이브로 명령어를 전송해야 슬레이브가 응답하는 방식)
  • 센서가 결함이 감지되지 않고 작동 중인지 확인하기 위해 주기적으로 센서의 상태를 체크해야 할 필요가 있음
  • ABC(Automatic Baseline Correction) 알고리즘 내장으로 일반적인 환경 하에선 유지보수로부터 자유로움
  • 미리 설정된 시간 간격동안 최하 판독값을 지속적으로 추적하고 예상된 신선한 공기에서의 CO2 값인 400ppm과 비교해 감지된 장기간 흐름을 천천히 수정함

 

[UART 설정]

  • 데이터 비트 길이 : 8비트 (패리티 비트 사용 X)
  • 스탑 비트 길이는 1, 2 비트 둘 다 사용 가능

  • 인터럽트 활용해 데이터 전송 및 수신

 

[데이터 전송 방법]

  • 기본적으로 마스터에서 센서로 데이터 버퍼를 보내면 센서가 이에 응답하는 방식
  • 센서의 각 레지스터는 16비트로 구성되어 있다
  • 센서로 보내는 데이터 버퍼는
  • 센서 주소(1) + 명령어(1) + 레지스터 시작 주소(2) + 읽어들일 레지스터 갯수(2) + CRC(2) 형태를 갖춘다
  • (괄호 안은 데이터의 크기 (바이트 기준))
  • 레지스터 시작 주소, 읽어들일 레지스터의 갯수, CRC는 상위1+하위1 총 2바이트로 나누어 전송해야 한다
  • 센서에서 마스터로 전송되는 데이터 버퍼는
  • 센서 주소(1) + 명령어(1) + 수신될 데이터의 바이트 크기(1) + 레지스터 데이터(레지스터 갯수*2) + CRC(2)
  • (읽어들일 레지스터 갯수에 맞춰 버퍼의 크기를 설정해야 한다)
  • CRC 값은 해당 데이터 버퍼의 0~CRC 직전까지의 데이터를 계산한 결과이므로 센서 응답의 CRC값은 유동적이다

1. CO2 값만을 읽어 올 때

  • 다음의 8바이트를 센서로 전송
  • <FE><04><00><03><00><01><C5><D5>
  • 센서 주소 : 0xFE
  • 명령어 : 0x04 (CO2 값이 저장된 인풋 레지스터를 읽어오는데 사용하는 명령어)
  • 레지스터 시작 주소 : 0x0003 (CO2 값이 저장된 인풋 레지스터3(0x03))
  • 레지스터 갯수 : 1 (0x0001)
  • CRC : D5C5 (하위 바이트인 C5 먼저 전송)
  • 예) 인풋 레지스터4(0x03)에 저장된 CO2 값(ppm 400(0x190h)이라 가정)을 읽어올 때
  • <FE><04><00><03><00><01><C5><D5> -총 8바이트를 전송
  • 센서는 이에 대한 응답으로
  • <FE><04><02><01><90><C5><D5> - 총 7바이트를 마스터로 전송
  • 센서 주소 + 명령어 + 전송되는 바이트 수(레지스터 갯수*2) + CO2 값 Hi + CO2 값 Lo + CRC Hi + CRC Lo

 

2. 센서 상태만을 읽어 올 때

  • 다음의 8바이트를 센서로 전송
  • <FE><04><00><00><00><01><25><C5>
  • 센서 상태는 인풋 레지스터1(0x00)에 저장되어 있으므로 인풋 레지스터를 읽는데 사용되는 0x04 명령어와 시작 주소 0x0000를 2바이트로 나누어 전송
  • 인풋 레지스터1에만 센서 상태가 저장되어 있으므로 읽어 올 레지스터의 갯수는 1 (0x0001)
  • CRC값 C525는 하위 25부터 전송
  • 센서는 다음과 같은 7바이트 데이터로 응답한다
  • <FE><04><02><00><00><AD><24>
  • 02는 전송되는 바이트의 수(레지스터 갯수*2)
  • 4,5번째의 <00><00>은 현재 센서의 에러 상태를 나타낸다

 

3. 센서와 CO2값을 한번에 읽어 올 때

  • 센서 상태는 인풋 레지스터1, CO2값은 인풋 레지스터4에 저장되므로 0x00~0x03까지 4개의 레지스터를 읽어와야 한다
  • <FE><04><00><00><00><04><E5><C6>
  • <FE>는 센서 주소(254), <04>는 인풋 레지스터 읽기 명령어인 0x04
  • 3,4 번째 <00><00>은 시작 주소 인풋 레지스터1 주소인 0x0000을 나타냄
  • 5,6 번째 <00><04>는 읽어 올 레지스터의 갯수인 4를 나타냄
  • <E5><C6>은 CRC 값
  • 센서는 다음과 같은 13바이트 데이터로 응답
  • <FE><04><08><00><00><00><00><00><00><01><90><16><E6>
  • 3번째 <08>은 전송될 데이터의 바이트 크기가 8바이트임을 의미 (레지스터 갯수 4*2)
  • 4,5번째 <00><00>은 인풋 레지스터1에 저장된 센서 상태를 나타냄
  • 6~7, 8~9는 각각 인풋 레지스터2(알람 상태), 3(아웃풋 상태)을 나타냄
  • 10,11번째 <01><90>은 측정된 CO2값을 나타냄 (0x190=400ppm)
  • <16><E6>은 CRC값

[코드]

- STM32Cube - Repository 내 Examples 중 UART_TwoBoards_ComIT 를 참조

- 센서의 CO2 측정 간격은 4초이므로 CO2 값을 4초 미만의 간격으로 읽어 올 필요는 없다

 

- CO2값을 읽어 올 때

//CO2 값을 읽기 위해 S8 센서로 보내는 데이터 버퍼
uint8_t send_Buffer[8]={0xFE, 0x04, 0x00, 0x03, 0x00, 0x01, 0xD5, 0xC5};

//S8 센서로부터 받아오는 데이터를 저장하기 위한 13바이트 데이터 버퍼
uint8_t receive_Buffer[7]={0};	

//상,하 1바이트씩 나뉘어 수신되는 CO2 값을 하나로 합치는데 사용
uint16_t co2_Value;	

//CO2 값 읽어오기 위한 데이터 버퍼를 센서로 전송
//데이터 전송에 사용할 UART, 전송할 버퍼, 전송할 데이터의 바이트 크기
if(HAL_UART_Transmit_IT(&hlpuart1,(uint8_t*)send_Buffer,8)!=HAL_OK) 
{
	Error_Handler();
}
//버퍼 전송 완료되기 전까지 대기
while(HAL_UART_GetState(&hlpuart1)==HAL_UART_STATE_BUSY_TX)
{
}
//버퍼 전송 완료 후, 센서 응답을 수신하기 위해 사용 중인 UART를 수신 상태로 둠
//데이터 수신에 사용할 UART, 전송된 데이터를 저장할 버퍼, 수신할 데이터의 바이트 크기
if(HAL_UART_Receive_IT(&hlpuart1,(uint8_t*)receive_Buffer,7)!=HAL_OK)
{
	Error_Handler();
}
//센서 응답 수신 완료까지 대기
while(HAL_UART_GetState(&hlpuart1)==HAL_UART_STATE_BUSY_RX)
{
}

//센서로부터 전달 받은 CO2 값의 상위 1바이트는 receive_Buffer[3], 하위 1바이트는 receive_Buffer[4]에 저장됨
//비트시프트를 사용하여 16비트 변수 co2_Value에 CO2 값을 하나로 합침
co2_Value=(receive_Buffer[3]<<8)|(receive_Buffer[4]);

CO2 측정 결과

 

-센서 상태를 읽어 올 때

//센서 상태를 읽기 위해 S8 센서로 보내는 데이터 버퍼
uint8_t send_Buffer[8]={0xFE, 0x04, 0x00, 0x00, 0x00, 0x01, 0x25, 0xC5};

//S8 센서로부터 받아오는 데이터를 저장하기 위한 7바이트 데이터 버퍼
uint8_t receive_Buffer[7]={0};	

//센서 상태 값 읽어오기 위한 데이터 버퍼를 센서로 전송
//데이터 전송에 사용할 UART, 전송할 버퍼, 전송할 데이터의 바이트 크기
if(HAL_UART_Transmit_IT(&hlpuart1,(uint8_t*)send_Buffer,8)!=HAL_OK) 
{
	Error_Handler();
}
//버퍼 전송 완료되기 전까지 대기
while(HAL_UART_GetState(&hlpuart1)==HAL_UART_STATE_BUSY_TX)
{
}
//버퍼 전송 완료 후, 센서 응답을 수신하기 위해 사용 중인 UART를 수신 상태로 둠
//데이터 수신에 사용할 UART, 전송된 데이터를 저장할 버퍼, 수신할 데이터의 바이트 크기
if(HAL_UART_Receive_IT(&hlpuart1,(uint8_t*)receive_Buffer,7)!=HAL_OK)
{
	Error_Handler();
}
//센서 응답 수신 완료까지 대기
while(HAL_UART_GetState(&hlpuart1)==HAL_UART_STATE_BUSY_RX)
{
}

//receive_Buffer[4]에 저장된 0~6 비트를 데이터 시트를 참고해 어떤 종류의 에러인지를 판단
if((receive_Buffer[4]&&1)==1)
{
	//Fatal error
}
else if((receive_Buffer[4]>>1)&&1==1)
{
	//Offset regulation error
}
else if((receive_Buffer[4]>>2)&&1==1)
{
	//Algorithm error
}
else if((receive_Buffer[4]>>3)&&1==1)
{
	//Output error
}    
else if((receive_Buffer[4]>>4)&&1==1)
{
	//Self diagnostics error
}
else if((receive_Buffer[4]>>5)&&1==1)
{
	//Out of Range
}
else if((receive_Buffer[4]>>6)&&1==1)
{
	//Memory error
}
else
{
	//No error
}

S8 센서 상태 확인 결과

+ Recent posts