ADC to CAN(Engine RPM) 테스트 코드

Tiny Project로 CAN 송수신 모듈을 만들고 있는데, 실시간 처리가 필요없는 것은 CLI를 사용하여 하였으나 엔진 RPM 같은 경우는 J1939 스펙상 10ms 단위로 지속적으로 보내야 하며, Engine RPM 값이 가변적으로 변해야 하므로 ADC로 입력 받은 값을 Engine RPM으로 설정하여 간단한 테스트 코드를 작성하였다.

STM32 핀 설정은 위와 같이 하였으며, 기존 GitHub의 CANTest2 설정에서 ADC IN7을 추가하였다. ADC 관련 부연 설명 및 관련 설정은 아래 블로그 자세히 다루고 있다. 잘 이해가 되지 않는다면 아래 동영상을 참고하면 더 좋을 것이다.

 

 

[STM32 HAL] ADC#Single Conversion

Reference: Mastering STM32 by Carmine Noviello ** 자료 해석에 오류가 있을 수 있습니다 ** [ 배...

blog.naver.com

실시간으로 엔진 RPM 정보를 변화하는 것을 간단히 테스트하기 위해 아래 이미지와 같이 저항을 추가하여, 이 값을 엔진 RPM 값으로 가정하여 CAN으로 송신하는 코드를 작성하려고 한다.

엔진 RPM을 나타내는 J1939 메시지 포멧은 EEC1이며, 이 메시지는 10ms마다 송신하기 때문에 1000ms 타이머를 추가하였으며, ADC1 및 TIM7의 파라미터는 아래 이미지와 같이 설정하였다.

ADC는 단일 채널 / 단일 변환이므로 위와 같이 설정하였으며, ADC 설정은 위 블로그를 참조하면 될 것이다. ADC 데이터를 폴링 방식으로 읽는다면 위 블로그 설정대로 하면 되지만, CPU 로드를 줄이기 위해 ADC 데이터를 DMA를 사용하여 읽을 경우 Continuous Conversion Mode를 Enable 시켜야 한다. TIM7 타이머는 90MHz으로 1ms 타이머를 생성하려면, Prescaler와 Period를 위와 같이 설정하면 된다.

 

ADC DMA 및 타이머 인터럽트를 아래와 설정해야 하며, CubeIDE Pinout 설정으로 대부분의 설정은 완료된 상태이기 때문에 인터럽트 핸들러 구현하면 된다.

int main(void)
{
    HAL_Init();
    ...
    
    /* USER CODE BEGIN 2 */
    HAL_CAN_Start(&hcan1);
    HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&adc_val, 1);
    HAL_TIM_Base_Start_IT(&htim7);
    /* USER CODE END 2 */

    while (1)
    {
        // uint16_t voltage = adc_val * 0.806; // mV
        // printf("adc_value = %4d, volate = %d.%d\r\n", adc_val, (voltage/1000), (voltage%1000));
        HAL_Delay(10);
    }
}

ADC에 타이머를 직접 연결하는 트리거(위 블로그 참고)로 인터럽트를 발생시킬 수 있지만 , 아래와 같이 1ms 타이머를 생성하여 특정 주기로 특정 함수를 실행하는 방법도 있다. 아래 소스는 10ms마다 eec1 메시지를 송신하는 코드이다.

static int can_tx_eec1(void)
{
    PGN_EEC1_t eec1;

    uint32_t priority = 0x03   << 26;
    uint32_t pgn      = 0xF004 << 8 ;   // Skipped EDP & DP 2bit
    uint32_t src_addr = 0x0;
    uint32_t ext_id   = priority | pgn | src_addr;

    eec1.engine_torque_mode                      = 14;
    eec1.actual_engine_percent_torque_fractional = 0;
    eec1.driver_demand_engine_percent_torque     = 254;
    eec1.actual_engine_percent_torque            = 125;
    eec1.engine_speed                            = adc_val / 0.125;
    eec1.source_address_of_ctrl_dev              = 187;
    eec1.engine_start_mode                       = 4;
    eec1.reserved6_b0                            = 0;
    eec1.reserved7                               = 125;

    return can_send_message(ext_id, (uint8_t *)&eec1);
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM7)
    {
        if((sys_tick % 10) == 0)
            can_tx_eec1();
        sys_tick++;
    }
}

TIM7 타이머는 1ms마다 인터럽트가 발생하기 때문에, sys_tick 값이 10으로 나누었을 때, 나머지가 없는 경우 eec1 메시지를 송신하도록 한 것이다. can_tx_eec1() 함수는 ADC에서 받은 값을 eec1의 engine speed(RPM) 값으로 변환하여 CAN 메시지를 송신한다. 전체 코드는 아래 GitHub에서 받을 수 있다.

 

highgon2/STM32

STM32 TestCode. Contribute to highgon2/STM32 development by creating an account on GitHub.

github.com