一、SENT背景介绍

提到车载总线,我们会立马想到经济可靠的CAN、Lin以及成本过高的FlexRay或Ethernet总线。但随着车载传感器数量的增加和对测量精度要求的提升,需要一种相比CAN或Lin更便捷、可靠、经济的车载数据通讯解决方案。

GM公司根据这种需求,首先制定了SENT标准,后来成为SAE J2716标准。随后一些公司在动力系统中逐渐采用该标准,并应用在整车传感器、执行器及Drive-by-wire线控等子系统中,总之目前越来越多的传感器都已支持SENT类型的信号。

从上面波形图我们可以看到SENT总线,其实就是通过改变占空比与频率来发送数据。采用STM32f103C8T6 来模拟输出数据,本实验通过TIM1_CH1(PA8)输出。

需要考虑的问题。

1:怎样同时修改频率与占空比呢?

2:怎样连续性的修改频率与占空比呢?

第一个问题很好搞定,修改定时器ARR与CCR1。

第二个问题怎样连续性修改了,肯定会想到定时器UP事件(CNT等于ARR)。当UP事件到来更新一组数据。配合DMA是不是代码变的更优雅了,准确率更高了。

//第一步 创建数组PWM

#define sent_tick  40     //定义一个tick时间长度   

uint16_t pwm_map[][3]={

 {(sent_tick*56-1),0,sent_tick*5},   //sync 56 ticks

 {(sent_tick*27-1),0,sent_tick*5},   //status/com  12-27 ticks

 {(sent_tick*12-1),0,sent_tick*5},   //data1 12-27 ticks  0

 {(sent_tick*13-1),0,sent_tick*5},   //data2 12-27 ticks  1

 {(sent_tick*14-1),0,sent_tick*5},   //data3 12-27 ticks  2

 {(sent_tick*15-1),0,sent_tick*5},   //data4 12-27 ticks  3

 {(sent_tick*16-1),0,sent_tick*5},   //data5 12-27 ticks  4

 {(sent_tick*17-1),0,sent_tick*5},   //data6 12-27 ticks  5

 {(sent_tick*18-1),0,sent_tick*5},   //crc   12-27 ticks  6

 {(sent_tick*77-1),0,sent_tick*5},   //pause pulse 77 ticks

 { sent_tick,0,0,}                   //结束

};

//第二步 定义SENT帧结构与CRC校验

typedef struct

{

 uint8_t status;

 uint8_t data[6];//nibble 4bit

 uint8_t crc;   //4bit

}SENT_Frame_t;

//校验计算

uint8_t crc4_cal(uint8_t *data,uint8_t len)

{

   const uint8_t CRC4_Table[16]= {0,13,7,10,14,3,9,4,1,12,6,11,15,2,8,5};

    uint8_t result  = 0x03;

    uint8_t tableNo = 0;

    int i = 0;

    for( ;i < len; i++)

    {

        tableNo = result ^ data[i];

        result  = CRC4_Table[tableNo];

    }

    return result;

}

//第三步 SENT数据转PWM数据

void pwm_to_sent(SENT_Frame_t frame)

{

  uint8_t i;

  pwm_map[1][0] = ((frame.status&0x0f)+12)*sent_tick;

 for(i=0;i<6;i++)

  {

    pwm_map[2+i][0] = ((frame.data[i]&0x0f)+12)*sent_tick;

  }

   frame.crc = crc4_cal(&frame.status,7);//状态+数据一起校验?还是只是数据

  pwm_map[8][0] = ((frame.crc&0x0f)+12)*sent_tick;

}

 //第四步 SENT数据发送

void sen_transmit(SENT_Frame_t frame)

{

    pwm_to_sent(frame);

HAL_TIM_DMABurst_MultiWriteStart(&htim1,TIM_DMABASE_ARR,TIM_DMA_UPDATE,(uint32_t *)pwm_map[0],TIM_DMABURSTLENGTH_3TRANSFERS,11*3);

}

//第五步 发送完成停产

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

{

  /* Prevent unused argument(s) compilation warning */

 // UNUSED(htim);

HAL_TIM_DMABurst_ReadStop(htim,TIM_DMA_UPDATE);

__HAL_TIM_CLEAR_FLAG(htim,TIM_FLAG_UPDATE);

  /* NOTE : This function should not be modified, when the callback is needed,

            the HAL_TIM_PeriodElapsedCallback could be implemented in the user file

   */

}

  

//连续发送SENT数据

int main(void)

{

SENT_Frame_t frame;

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();

  MX_DMA_Init();

  MX_TIM1_Init();

  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);

  while (1)

  {

        frame.status = 1;

        frame.data[0] = 2;frame.data[1] =3;frame.data[2] = 4;

       frame.data[3] = 5;frame.data[4] = 15;frame.data[5] = 0;

       sen_transmit(frame); //持续发送,发送完一帧马上发送第二帧

  }

}

最后一张示波器抓的图:

/**
  * @brief TIM1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM1_Init(void)
{

  /* USER CODE BEGIN TIM1_Init 0 */

  /* USER CODE END TIM1_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};

  /* USER CODE BEGIN TIM1_Init 1 */

  /* USER CODE END TIM1_Init 1 */
  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 63;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 2000;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 1000;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_SET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
  sBreakDeadTimeConfig.DeadTime = 0;
  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM1_Init 2 */

  /* USER CODE END TIM1_Init 2 */
  HAL_TIM_MspPostInit(&htim1);

}

/**
* @brief TIM_Base MSP Initialization
* This function configures the hardware resources used in this example
* @param htim_base: TIM_Base handle pointer
* @retval None
*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
  if(htim_base->Instance==TIM1)
  {
  /* USER CODE BEGIN TIM1_MspInit 0 */

  /* USER CODE END TIM1_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_TIM1_CLK_ENABLE();

    /* TIM1 DMA Init */
    /* TIM1_UP Init */
    hdma_tim1_up.Instance = DMA1_Channel5;
    hdma_tim1_up.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_tim1_up.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_tim1_up.Init.MemInc = DMA_MINC_ENABLE;
    hdma_tim1_up.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_tim1_up.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_tim1_up.Init.Mode = DMA_NORMAL;
    hdma_tim1_up.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_tim1_up) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_UPDATE],hdma_tim1_up);

  /* USER CODE BEGIN TIM1_MspInit 1 */

  /* USER CODE END TIM1_MspInit 1 */
  }

}

作者:一人科技

物联沃分享整理
物联沃-IOTWORD物联网 » STM32 模拟SENT总线

发表回复