SAE J1850 汽车总线协议 VPW 物理层驱动程序在STM32芯片上的实现

VPW(Variable Pulse Width)是一种可变脉宽调制的汽车总线通讯方式,常用于美系的福特,通用,克莱斯勒等汽车上,主要用途为车用信息中心、仪表显示、故障检测诊断等。

VPW – 以数据位为基本单位进行传输,定义了一个起始位(SOF):200us 的高电平代表开始进行位传输,定义了一个结束位(EOF):280us 的低电平表示位传输正常结束,起始位之后的数据位表示方式可认为为:电平不断的翻转,每次产生一次翻转便产生一个新的数据位,这个数据位为“0”还是“1”由翻转时电平的持续时间来决定,数据位“0”用 64us 的低电平或 128us 的高电平表示,数据位“1” 用 64us 的高电平或 128us 的低电平表示。另在网络节点多的时候VPW 针对链路层定义了有效数据域结束位(EOD),帧间仲裁时间(IFS),多数据域时接收节点的应答就绪响应时间(IFR)如下图所示:

以上为背景知识介绍,下面要根据vpw协议用stm32F429这颗芯片来实现这个协议。如果非要问为什么要用这么高级的芯片?我只能回答你:有钱,任性!

看了上面那张图,一般程序猿脑海里面应该都冒出了不少实现方式,一般来说,有如下几种:

1、普通程序猿:

咦?这不是很简单嘛,一根IO拉高或者拉低,中间delay(x us)即可实现发送,一根IO设置为外部中断引脚,有电平变化就进中断,然后打开一个TIM,电平翻转再进中断,读时间,OK搞定收工!

2、文艺程序猿:

嗯?楼上弱爆了,基本是小学生的水平,那个delay死循环那么低级,怎么能行呢,怎么样也得再开一个TIM用来定时才准嘛··· ···

3、二逼程序猿:

你们都让开,这逼我今天装定了!··· ···

没错,我就是那个二逼程序猿,接手了公司经过N手离职同事的文艺青年代码,没效率还容易出错,实在看不下去了,今天就来一次不走寻常路,好好研究一番这个VPW驱动的实现。

先看电路图,vpwin为输入引脚,j1850+为输出引脚,BUS P和N为实际总线,j1850-和pwmin用不着。

再分析上面第一张的逻辑图,vpw是通过逻辑的高低变化来表示0和1,因此,一个高低电平变化就能表示2bit数据,2bit数据有4种:00、01、10、11,通过观察发现任意2bit组合成的一个脉冲有如下关系(注意这里的表是根据实际总线电平来算的,cpu引脚经过上面的电路图后信号会反向,也就是高变低,低变高):

bit | 周期 | 脉宽 | 累加和

00 | 192 | 64 | 256

01 | 128 | 64 | 192

10 | 256 | 128 | 384

11 | 192 | 128 | 320

由上面的表可见,vpw不同于pwm的地方就是它的周期是会有变化的,pwm的周期是固定的,因此,想简单的用pwm来实现输出是不行的,不过,既然选择了不走寻常路,那肯定不能用简单的方法了,要不然,直接用文艺青年的方法就行了,还装什么逼!

说说总体思路:

发送部分:

1、开一个定时器TIM1,配置成PWM比较输出模式,通道我用的是CC1,

2、开启两个DMA通道,触发源为TIM1_DMA_update和TIM1_DMA_CC1

3、开2个同样大数组,一个是周期,一个是脉宽,大小为你要发送的最长数据的8倍,(一个数据为1个bit)

4、将你要发送的数据按顺序拆分为2bit一组,对照上面的关系图将周期和脉宽转换为定时器的pwm周期和脉宽值,填进表内,别忘了起始和结束脉冲

5、配置2个dma,发送数据量和数据源地址,地址为两个数组地址,打开dma的发送完成中断,打开TIM1 CC1,

6、剩下的事情就是让TIM和DMA自己嗨,等到程序自己进入了dma发送完成中断就是发送完成了

接收部分:

1、开一个定时器TIM4,配置成输入捕获模式,通道CC1和CC2

2、开启两个DMA通道,触发源为TIM_DMA_CC1和TIM_DMA_CC2

3、开启两个数组,同发送部分一样

4、启动tim的输入捕获和dma,等着vpw引脚信号到来,dma会自己将信号的周期和脉宽放入开好的数组内

5、对照上面的关系图,将捕获到的脉宽和周期还原成原始数据,搞定收工!

以上方式除了数据的拆分和还原以外,所有发送和接收过程均有硬件自己完成,只要定时器配置得精准,发送出去的波形就非常精准漂亮,捕获的精度也很高,并且不会死死霸占cpu的时间

废话多了,没有代码你说个JB!按照国际范例还是上点代码吧,上个最主要的配置代码,其他的自己发挥了,仅供参考,后果不负责!

/*
*********************************************************************************************************
*                                          BSP_J1850_PWM_Init()
*
* Description : 在tim溢出的时候触发DMA将新的占空比数值自动传入tim,发送完成后产生dma中断关闭tim
*
* Argument(s) :
*
* Return(s)   : none.
*
* Caller(s)   : Application.
*
* Note(s)     : none.
*********************************************************************************************************
*/

void  BSP_J1850_VPW_Init (void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;
    TIM_ICInitTypeDef  TIM_ICInitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure ;                                       

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource8 , GPIO_AF_TIM1);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource7 , GPIO_AF_TIM4); 

    GPIO_InitStructure.GPIO_Pin          = GPIO_Pin_7 ;                         //  GPIOB7_VPW_IN
    GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed        = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType        = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP ;
    GPIO_Init(GPIOB, &GPIO_InitStructure);    

    GPIO_InitStructure.GPIO_Pin          = GPIO_Pin_8;                          //GPIOA8_J1850_P ;
    GPIO_Init(GPIOA, &GPIO_InitStructure);  

    GPIO_InitStructure.GPIO_Pin          = GPIO_Pin_12 | GPIO_Pin_13;            //GPIOB12 PWM_VOL GPIOB13_J1850_N;
    GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_OUT;
    GPIO_Init(GPIOB, &GPIO_InitStructure); 

    NVIC_InitStructure.NVIC_IRQChannel                  = DMA2_Stream5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority       = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd               = ENABLE;
    NVIC_Init(&NVIC_InitStructure); 

    NVIC_InitStructure.NVIC_IRQChannel                  = TIM4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority       = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd               = ENABLE;
    NVIC_Init(&NVIC_InitStructure); 

    NVIC_InitStructure.NVIC_IRQChannel                  = TIM1_CC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority       = 3;
    NVIC_InitStructure.NVIC_IRQChannelCmd               = ENABLE;
    NVIC_Init(&NVIC_InitStructure); 

    TIM4_IRQ    =       BSP_VPW_TIM4_ISR_Handler;
    TIM1_CC_IRQ =       BSP_VPW_TIM1_ISR_Handler;
    DMA2_Stream5_IRQ=   BSP_VPW_DMA_ISR_Handler;  

     //pwm out
    RCC_TIMCLKPresConfig(RCC_TIMPrescActivated);

    TIM_DeInit(TIM1);
    TIM_TimeBaseStructure.TIM_Period      = PERIOD_SOF;
    TIM_TimeBaseStructure.TIM_Prescaler   = 0;
    TIM_TimeBaseStructure.TIM_ClockDivision= TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

    TIM_ARRPreloadConfig(TIM1, ENABLE);
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); 

    TIM_OCInitStructure.TIM_OCMode        = TIM_OCMode_PWM2;
    TIM_OCInitStructure.TIM_OutputNState  = TIM_OutputNState_Enable;
    TIM_OCInitStructure.TIM_OutputState   = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse         = PULSE_SOF;
    TIM_OCInitStructure.TIM_OCNPolarity   = TIM_OCNPolarity_Low;
    TIM_OCInitStructure.TIM_OCNIdleState  = TIM_OCNIdleState_Reset;
    TIM_OCInitStructure.TIM_OCPolarity    = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_OCIdleState   = TIM_OCIdleState_Set;  

    TIM_OC1Init(TIM1, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Disable);      

    //vpw out dma
    DMA_DeInit(DMA2_Stream5);   //up
    DMA_DeInit(DMA2_Stream1);   //ch1
    while(DMA_GetCmdStatus(DMA2_Stream5)!=DISABLE);
    while(DMA_GetCmdStatus(DMA2_Stream1)!=DISABLE);

    DMA_InitStructure.DMA_Channel               = DMA_Channel_6;
    DMA_InitStructure.DMA_PeripheralBaseAddr    = (uint32_t)0x40010034;         //TIM1_CC1
    DMA_InitStructure.DMA_Memory0BaseAddr       = (uint32_t)(&(VPW_Data.Pulse[vpw_buf][0]));
    DMA_InitStructure.DMA_DIR                   = DMA_DIR_MemoryToPeripheral;
    DMA_InitStructure.DMA_BufferSize            = VPW_Data.Num_Pulse[vpw_buf];
    DMA_InitStructure.DMA_PeripheralInc         = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc             = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize    = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize        = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode                  = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority              = DMA_Priority_High;
    DMA_InitStructure.DMA_FIFOMode              = DMA_FIFOMode_Enable;
    DMA_InitStructure.DMA_FIFOThreshold         = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst           = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst       = DMA_PeripheralBurst_Single;

    DMA_Init(DMA2_Stream5, &DMA_InitStructure);  

    DMA_InitStructure.DMA_PeripheralBaseAddr    = (uint32_t)0x4001002C;  //TIM1_ARR
    DMA_InitStructure.DMA_Memory0BaseAddr       = (uint32_t)(&(VPW_Data.Period[vpw_buf][0]));
    DMA_InitStructure.DMA_BufferSize            = VPW_Data.Num_Period[vpw_buf];
    DMA_Init(DMA2_Stream1, &DMA_InitStructure);

    DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5);
    DMA_ClearFlag(DMA2_Stream1,DMA_FLAG_TCIF1);

    DMA_ITConfig(DMA2_Stream5, DMA_IT_TC, ENABLE);

    TIM_ClearITPendingBit(TIM1,TIM_IT_CC1 );
    TIM_ClearITPendingBit(TIM1,TIM_IT_Update );

    TIM_DMACmd(TIM1, TIM_DMA_Update, ENABLE);//一个改变周期
    TIM_DMACmd(TIM1, TIM_DMA_CC1, ENABLE);   //一个改变占空比
 //   TIM_Cmd(TIM1, ENABLE);   

    //vpw in
    TIM_DeInit(TIM4);
    TIM_TimeBaseStructure.TIM_Period        = 65535;
    TIM_TimeBaseStructure.TIM_Prescaler     = 0;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up ;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

    TIM_ARRPreloadConfig(TIM4, DISABLE);
    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);  

    TIM_ICInitStructure.TIM_Channel     = TIM_Channel_2;
    TIM_ICInitStructure.TIM_ICPolarity  = TIM_ICPolarity_Falling;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICFilter    = 0x2;   

    TIM_PWMIConfig(TIM4, &TIM_ICInitStructure);
    TIM_ITConfig(TIM4, TIM_IT_CC2, ENABLE);  

    TIM_DMACmd(TIM4, TIM_DMA_CC1, ENABLE);
    TIM_DMACmd(TIM4, TIM_DMA_CC2, ENABLE);

    TIM_SelectInputTrigger(TIM4, TIM_TS_TI2FP2);
    TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset);
    TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable);
    TIM_Cmd(TIM4, ENABLE);

    //vpw in dma
    DMA_DeInit(DMA1_Stream0);   //cc1
    DMA_DeInit(DMA1_Stream3);   //cc2
    while(DMA_GetCmdStatus(DMA1_Stream0)!=DISABLE);
    while(DMA_GetCmdStatus(DMA1_Stream3)!=DISABLE);

    DMA_InitStructure.DMA_Channel               = DMA_Channel_2;
    DMA_InitStructure.DMA_PeripheralBaseAddr    = (uint32_t)0x40000834;         //TIM4_CCR1
    DMA_InitStructure.DMA_Memory0BaseAddr       = (uint32_t)(&(VPW_Data.Pulse[vpw_buf][0]));
    DMA_InitStructure.DMA_DIR                   = DMA_DIR_PeripheralToMemory ;
    DMA_InitStructure.DMA_BufferSize            = VPW_MAX;
    DMA_InitStructure.DMA_PeripheralInc         = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc             = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize    = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize        = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode                  = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority              = DMA_Priority_High;
    DMA_InitStructure.DMA_FIFOMode              = DMA_FIFOMode_Enable;
    DMA_InitStructure.DMA_FIFOThreshold         = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst           = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst       = DMA_PeripheralBurst_Single;

    DMA_Init(DMA1_Stream0, &DMA_InitStructure); 

    DMA_InitStructure.DMA_PeripheralBaseAddr     = (uint32_t)0x40000838;        //tim4 CCR2
    DMA_InitStructure.DMA_Memory0BaseAddr        = (uint32_t)(&(VPW_Data.Period[0]));
    DMA_Init(DMA1_Stream3, &DMA_InitStructure);   

}
时间: 2024-10-28 08:34:12

SAE J1850 汽车总线协议 VPW 物理层驱动程序在STM32芯片上的实现的相关文章

I2C总线协议的软件模拟实现方法

I2C总线协议的软件模拟实现方法 在上一篇博客中已经讲过I2C总线通信协议,本文讲述I2C总线协议的软件模拟实现方法. 1. 简述 所谓的I2C总线协议的软件模拟实现方法,就是用软件控制GPIO的输入.输出和高低电平变化,来模拟I2C总线通讯过程中SCL.SDA的电平变化来实现的. 2. I2C总线的封装 每个处理器对应的GPIO操作都有差异,即使是同一款处理器,不同的人也会有不同的GPIO封装风格,就以我个人习惯用的GPIO方法为例来进行讲解.我习惯上将GPIO的组和位封装为一个结构体,这样使

SAE J1850 VPW Library on PIC16F877(A) @ 20MHz

-- File: VPWM.jal -- Author: Shawn Standfast -- Version 1.00.2 -- 11/1/2005 -- Library for OBDII Variable Pulse Width Modulation on PIC16F877(A) @ 20MHz -- -- *Note - This library utilizes the CCPX Module, Timer 1, Timer 2 W/ Interrupts -- Also, be s

汽车总线特性简述

汽车上的各种总线/线束技术其主要目的是通信/控制. 1. 车辆通信区域 而车辆通信区域包括以下几个部分: 动力总成: 引擎, 传动装置 底盘: 转向装置, ABS, 胎压 车身: 门, 灯, 座椅, A/C 安全: 气囊, 传感器, 感应启动器, OSS(乘员安全系统), ADAS, ESP 信息娱乐和辅助驾驶: 导航, Telematics, TV/Radio/CD/DVD, 后排座椅娱乐系统, 摄像头 这些区域的通信的需求各自又不尽相同,所以就产生了各种总线技术. 2. 汽车总线有哪些类型?

LIN、CAN、FlexRay、MOST,三分钟搞明白四大汽车总线

LIN.CAN.FlexRay.MOST,三分钟搞明白四大汽车总线 2016-09-21 13:09 汽车中的电子部件越来越多,光是ECU就有几十个,这么多的电子单元都要进行信息交互.传统的点对点通信已经不能满足需求,因此必须要采用先进的总线技术. 车用总线就是车载网络中底层的车用设备或车用仪表互联的通信网络.目前,有四种主流的车用总线:CAN总线.LIN总线.FlexRay总线和MOST总线. 用一张表格来说明各种总线的区别 LIN总线 LIN(Local Interconnect Netwo

IIC总线协议

 II2C C总线的简单的概述 1.( 总线(Inter Integrated Circuit Bus Inter Integrated Circuit Bus):是 ):是Philips Philips公司 公司 推出的串行总线标准(为二线制).总线上扩展的外围器件及外设接 推出的串行总线标准(为二线制). 2.总线上扩展的外围器件及外设接 口通过总线寻址,是具备总线仲裁和高低速设备同步等功能的高性能 口通过总线寻址,是具备总线仲裁和高低速设备同步等功能的高性能 多主机总线. 3.特点:组成系

[I2C]I2C总线协议图解

转自:http://blog.csdn.net/w89436838/article/details/38660631 1  I2C总线物理拓扑结构      I2C 总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成.通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递.在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平. 2  I2C总线特征 I2C总线上的每一个设备都可以作为主设备或者从设备

SPI、I2C、UART三种串行总线协议的区别和SPI接口介绍(转)

SPI.I2C.UART三种串行总线协议的区别 第一个区别当然是名字: SPI(Serial Peripheral Interface:串行外设接口); I2C(INTER IC BUS) UART(Universal Asynchronous Receiver Transmitter:通用异步收发器) 第二,区别在电气信号线上: SPI总线由三条信号线组成:串行时钟(SCLK).串行数据输出(SDO).串行数据输入(SDI).SPI总线可以实现多个SPI设备互相连接.提供SPI串行时钟的SPI

SPI总线协议及SPI时序图详解

SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口.SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议.SPI是一个环形总线结构,由ss(cs).sck.sdi.sdo构成,其时序其实很简单,主要是在sck的控制下,两个双向移位寄存器进行数据交换. 上升沿发送.下降沿接收.高位先发送.上升沿到来

SPI总线协议分析

一.概述 SPI, Serial Perripheral Interface, 串行外围设备接口, 是 Motorola 公司推出的一种同步串行接口技术. SPI 总线在物理上是通过接在外围设备微控制器(PICmicro) 上面的微处理控制单元 (MCU) 上叫作同步串行端口(Synchronous Serial Port) 的模块(Module)来实现的, 它允许 MCU 以全双工的同步串行方式, 与各种外围设备进行高速数据通信. SPI 主要应用在 EEPROM, Flash, 实时时钟(R