본문 바로가기

STM32

STM32-CAN-Raspberry Pi

동작

STM32에서 DHT22의 온습도 값을 CAN을 통해 Raspberry Pi로 전송.

 

구조

STM32(FreeRTOS)

Task1: DHT22의 온습도 값을 주기적으로 읽어 Task2로 값 전달.

Task2: 전달 된 온습도 값을 CAN transmit 하고 OLED display 에 출력.

 

Raspberry Pi

CAN ID 모니터링, 수신된 데이터를 콘솔 출력.

 

하드웨어 구성

main 함수

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include <stdio.h>
#include <string.h>
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <float.h>
#include <dht22.h>
#include "4ilo/4ilo_ssd1306.h"
/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/
CAN_HandleTypeDef hcan1;
TIM_HandleTypeDef htim6;

SemaphoreHandle_t xSemaphore = NULL;

/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityBelowNormal,
};
/* Definitions for dht22Task */
osThreadId_t dht22TaskHandle;
const osThreadAttr_t dht22Task_attributes = {
  .name = "dht22Task",
  .stack_size = 256 * 4,
  .priority = (osPriority_t) osPriorityNormal3,
};
/* Definitions for CanReceiveTask */
osThreadId_t CanReceiveTaskHandle;
const osThreadAttr_t CanReceiveTask_attributes = {
  .name = "CanReceiveTask",
  .stack_size = 256 * 4,
  .priority = (osPriority_t) osPriorityNormal1,
};
/* Definitions for displayTask */
osThreadId_t displayTaskHandle;
const osThreadAttr_t displayTask_attributes = {
  .name = "displayTask",
  .stack_size = 256 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};

/* USER CODE BEGIN PV */
CAN_FilterTypeDef canFilter; // CAN Bus filter
QueueHandle_t dht22Queue = NULL;

int main(void)
{
  xSemaphore = xSemaphoreCreateBinary();
  /* USER CODE BEGIN RTOS_TIMERS */
  setTimer(&htim6);
  HAL_TIM_Base_Start(&htim6);
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  dht22Queue = xQueueCreate(10, sizeof(DHT_DataTypedef));
  if(dht22Queue == NULL) {
    printf("Failed to create queue\n\r");
    while(1) {
      osDelay(1);
    }
  }
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* creation of defaultTask */
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  /* creation of dht22Task */
  dht22TaskHandle = osThreadNew(StartDHT22Task, NULL, &dht22Task_attributes);
  
  /* creation of CanReceiveTask */
  CanReceiveTaskHandle = osThreadNew(StartCanReceiveTask, NULL, &CanReceiveTask_attributes);

  /* creation of displayTask */
  displayTaskHandle = osThreadNew(StartDisplayTask, NULL, &displayTask_attributes);

}

 

DHT22 온습도 값 처리 태스크

void StartDHT22Task(void *argument)
{
  /* USER CODE BEGIN StartDHT22Task */
  vTaskDelay(pdMS_TO_TICKS(300)); // wait for display task
  DHT_DataTypedef dht22Data;

  /* Infinite loop */
  for(;;)
  {
    dht22Data.Humidity = FLT_MAX;
    dht22Data.Temperature = FLT_MIN;
    if(DHT_GetData(&dht22Data) == 0) {
      printf("DHT_GetData() error\n\r");
    } else if(xQueueSend(dht22Queue, &dht22Data, pdMS_TO_TICKS(100)) != pdPASS) {
      printf("Failed to send data to queue\n\r");
    }
    vTaskDelay(pdMS_TO_TICKS(3000));
  }
  /* USER CODE END StartDHT22Task */
}

 

CAN Transmit & display 처리 태스크

void vMainCanTransmit(CAN_HandleTypeDef *handle, DHT_DataTypedef* dhtData)
{
  CAN_TxHeaderTypeDef txHeader; // CAN Bus Receive header
  uint32_t txMailbox; // CAN Bus Mail box variable
  uint8_t txData[5];
  uint32_t intPart, fractPart;

  txHeader.IDE = CAN_ID_STD;
  txHeader.RTR = CAN_RTR_DATA;
  txHeader.StdId = 0x030;
  txHeader.ExtId = 0x03;
  txHeader.TransmitGlobalTime = DISABLE;
  txHeader.DLC = 5;

  intPart = (uint32_t)dhtData->Humidity;
  fractPart = (uint32_t)((dhtData->Humidity - (float)intPart) * 100.0f);
  txData[0] = (uint8_t)(intPart % 100);
  txData[1] = (uint8_t)fractPart;
  intPart = (uint32_t)dhtData->Temperature;
  fractPart = (uint32_t)((dhtData->Temperature - (float)intPart) * 100.0f);
  txData[2] = (uint8_t)(intPart % 100);
  txData[3] = (uint8_t)fractPart;
  txData[4] = ++canMsgCount;
  printf("CAN send [%02d,%02d,%02d,%02d,%02d]\n\r",
    txData[0], txData[1], txData[2], txData[3], txData[4]);
  
  // check if mailboxes are free
  uint32_t timeout = pdMS_TO_TICKS(100);
  TickType_t start = xTaskGetTickCount();
  while(HAL_CAN_GetTxMailboxesFreeLevel(handle) != 3) {
      if((xTaskGetTickCount() - start) > timeout) {
          // timeout
          printf("CAN transmit timeout\n\r");
          break;
      }
      taskYIELD();
  }

  if(HAL_CAN_AddTxMessage(&hcan1, &txHeader, txData, &txMailbox) != HAL_OK) {
    printf("CAN transmit error\n\r");
    return;
  }

  HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
}


void StartDisplayTask(void *argument)
{
  /* USER CODE BEGIN StartDisplayTask */
  DHT_DataTypedef dht22Data;
  char humidity[16];
  char temperature[16];

  // Init lcd using one of the stm32HAL i2c typedefs
  if(ssd1306_Init(&hi2c1) != 0) {
    Error_Handler();
  }
  vTaskDelay(pdMS_TO_TICKS(100));

  /* Infinite loop */
  for(;;) {
    if(xQueueReceive(dht22Queue, &dht22Data, pdMS_TO_TICKS(100)) == pdPASS) {
      vMainCanTransmit(&hcan1, &dht22Data);
      memset(humidity, 0, 16);
      memset(temperature, 0, 16);
      snprintf(humidity, 16, "H %.2f", dht22Data.Humidity);
      snprintf(temperature, 16, "T %.2f", dht22Data.Temperature);
      printf("[DT] t:%s h:%s\n\r", temperature, humidity);
      
      ssd1306_Fill(Black);
      ssd1306_UpdateScreen(&hi2c1);
    
      ssd1306_SetCursor(0, 0);
      ssd1306_WriteString(humidity, Font_7x10, White);
    
      ssd1306_SetCursor(0, 12);
      ssd1306_WriteString(temperature, Font_7x10, White);
    
      ssd1306_UpdateScreen(&hi2c1);
    }
    vTaskDelay(pdMS_TO_TICKS(500));
  }
  /* USER CODE END StartDisplayTask */
}

 

CAN 데이터 수신 처리

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
  if(hcan->Instance == CAN1) {
    if(HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rxHeader, rxData) != HAL_OK) {
      /* Reception Error */
      return;
    }
    can_received = 1; // CAN Bus received flag
  }
}

 

Raspberry pi CAN 데이터 수신 처리

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <errno.h>

#define CAN_INTERFACE "can0"
const unsigned char response_data[5] = {0x0F, 0xF0, 0x0F, 0xF0, 0x00};
#define RESPONSE_DLC sizeof(response_data)

int main() {
    int s;
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct can_frame frame;
    int nbytes;

    struct can_filter rfilter[] = {
        { .can_id = 0x30, .can_mask = CAN_SFF_MASK }
    };
    int num_filters = sizeof(rfilter) / sizeof(struct can_filter);

    s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if(s < 0) {
        perror("failed to create socket");
        return 1;
    }

    strcpy(ifr.ifr_name, CAN_INTERFACE);
    if(ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
        perror("ioctl error");
        close(s);
        return 1;
    }

    if(setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter)) < 0) {
        perror("setsockopt error");
        close(s);
        return 1;
    }

    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind error");
        close(s);
        return 1;
    }

    printf("'%s' connected. waiting...\n", CAN_INTERFACE);
    
    resp_frame.can_id = RESPONSE_CAN_ID;
    resp_frame.can_dlc = RESPONSE_DLC;
    memcpy(resp_frame.data, response_data, RESPONSE_DLC);

    while(1) {
        nbytes = read(s, &frame, sizeof(struct can_frame));

        if(nbytes < 0) {
            if(errno == ENETDOWN) {
                fprintf(stderr, "network down?\n");
                break;
            }
            perror("CAN RAW read error");
            continue;
        } else if(nbytes < sizeof(struct can_frame)) {
            fprintf(stderr, "not completed CAN received: %d bytes\n", nbytes);
            continue;
        }

        printf("\nReceived ");
        printf("ID:0x%X, ", frame.can_id & CAN_SFF_MASK);
        printf("DLC:%d\n", frame.can_dlc);
        printf("data:");
        for(int i = 0; i < frame.can_dlc; i++) {
            printf(" %02X", frame.data[i]);
        }
        printf("\n");
        printf("Humidity:%02d.%02d, Temperature:%02d.%02.d\n",
            frame.data[0], frame.data[1], frame.data[2], frame.data[3]);
            
        resp_frame.data[4] = frame.data[4];
        
        nbytes = write(s, &resp_frame, sizeof(struct can_frame));
        if (nbytes < 0) {
            perror("CAN RAW write error");
        } else {
             printf("sent response.\n");
        }
    }

    close(s);
    printf("'%s' closed. bye.\n", CAN_INTERFACE);

    return 0;
}

출력결과

'STM32' 카테고리의 다른 글

DHT22 온습도 센서  (0) 2025.03.23
OLED display 128x32  (0) 2025.02.28
STM32 clock  (0) 2025.01.23
FreeRTOS - STM32F4 포팅 정보 확인  (0) 2025.01.14
FreeRTOS 와 Zephyr 비교  (0) 2025.01.13