STM32 模拟SENT总线
一、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 */
}
}
作者:一人科技