原文地址::https://blog.csdn.net/lrmlrm/article/details/51499421

相关文章

1、VPW协议解析----https://www.cnblogs.com/shangdawei/p/3556576.html?utm_source=tuicool&utm_medium=referral

2、J1850_百度百科----https://baike.baidu.com/item/J1850/5057293?fr=aladdin

3、obd转J1850VPW原理图PCB源码----https://download.csdn.net/download/q2395305775/6272815

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);

}
————————————————
版权声明:本文为CSDN博主「听我吹牛逼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lrmlrm/article/details/51499421

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

  1. 空调控制协议盒_睿志诚汽车总线协议盒 后装车机也能控制车辆

    车内加装了导航车机后,我们会发现通过车机也能控制车辆空调.车灯.门窗等信息,那么到底车机是通过什么实现的呢?据悉,只需要在车机内接入睿志诚Can-Bus汽车总线协议盒,这些功能就能"信手拈来 ...

  2. gprs模块http mqtt_在GPRS模块(SIM800C)和STM32芯片上实现MQTT协议 | TsonTec:测量解决方案提供者...

    最近真是的好一个劲的折腾,算是完全搞明白了如何在STM32上实现MQTT协议了. 目录 [显示] 一.本教程中说明的内容 先说说本文化的适用范围吧: 一.使用的芯片是STM32F103C8T6,但是并 ...

  3. POWERLINK协议源码(最新)在stm32单片机上的移植指南

    最近着了powerlink的道,连续几晚十二点前没睡过觉.不得不说兴趣这东西劲太大了,让人睡不着.喜欢上研究POWERLINK,最新版的源码结构挺清晰的,移植并测试了嵌入式linux作为从站和电脑主站 ...

  4. 细说汽车电子通信总线之CAN 2.0 总线协议详解

    引言 1. CAN总线发展历史与ISO规范 2. CAN总线主要功能特性 3. CAN 2.0总线协议的物理层电气特性 4. CAN 2.0总线协议消息报文详解 4.1 CAN2.0总线的通信报文帧格 ...

  5. 细说汽车电子通信总线之LIN总线协议详解

    内容提要 1. LIN总线概述 1.1 LIN总线特点及发展历史 1.2 LIN 总线协议的物理层(收发器) 2. LIN总线报文帧结构(Message Frame Structure)详解 2.1 ...

  6. can总线不加末端电阻_细说汽车电子通信总线之CAN 2.0 总线协议详解

    引言 1. CAN总线发展历史与ISO规范 2. CAN总线主要功能特性 3. CAN 2.0总线协议的物理层电气特性 4. CAN 2.0总线协议消息报文详解 4.1 CAN2.0总线的通信报文帧格 ...

  7. CAN总线协议(一)

    1.1 CAN 协议简介 CAN 是控制器局域网络 (Controller Area Network) 的简称,它是由研发和生产汽车电子产品著称的德国 BOSCH 公司开发的,并最终成为国际标准(IS ...

  8. pci总线定时协议_汽车总线测试的“解忧杂货店”

    "我的回答之所以发挥了作用,原因不是别的,是因为大家自己很努力." -- 东野圭吾<解忧杂货店> 相信很多读者都看过东野圭吾的书<解忧杂货店>或者同名电影, ...

  9. 汽车诊断协议,(K线/CAN总线、kwp2000、ISO14230、ISO1575...)

    相信像我这样的小白第一次接触汽车诊断协议肯定有点懵逼,什么鬼kwp2000,那什么又是ISO-14230,ISO-15765,ISO-14229,UDS,UDSonCAN???它们到底是什么关系,还有 ...

最新文章

  1. WinCE5.0中应用程序如何直接写屏
  2. 人民日报智慧媒体研究院与第四范式合资成立智媒新创 赋能智慧媒体创新
  3. Detectron:Pytorch-Caffe2-Detectron的一些跟进
  4. java 对象克隆_JAVA对象克隆
  5. rmlv出错处理一例
  6. lua代码格式化工具_lua的代码覆盖率工具
  7. 阿里巴巴 CTO 首次分享技术战略
  8. html5查询通配符,通配符有哪些?
  9. HTML5 实例:旋转同时放大缩小的方块(canvas)
  10. 利用java图形用户界面实现后台管理系统
  11. Java-常用实现分页查询
  12. Java开发Mye_Java使用MyEclipse构建webService简单案例
  13. 数据库分区:MySQL分区
  14. ROS(ERROR):Rosdep cannot find all required resources to answer your query
  15. 2016版excel_Python使用openpyxl和pandas处理Excel文件实现数据脱敏案例一则
  16. Android 打包后apk安装失败
  17. Pytorch 构建简单Neural Networks
  18. keil c语言字符串赋值,keil c指针变量赋值 请指点下迷津
  19. DVDStyler(dvd制作工具)官方中文版V3.2 | 制作dvd视频光盘软件下载 | 怎么制作dvd视频光盘
  20. 已经三十多岁了还是一事无成,还能靠做自媒体翻身吗?

热门文章

  1. vivox21支持html,vivo X21支持双卡吗_vivo X21支持双卡双待吗-太平洋IT百科
  2. linux opencv 显示视频文件夹,opencv cvCaptureFromFile读取视频问题汇总
  3. 计算机外观保养的建议,网际电脑如何建议客户您怎样保养电脑
  4. TIM微秒级计时学习笔记
  5. 利用百度地图采集大量某一区域经纬度信息
  6. 无法访问计算机无效的语法,您输入的表达式包含无效语法(The expression you entered contains invalid syntax)...
  7. 泰课在线android,泰课在线rollaball
  8. 什么是接口,面向接口编程的好处是什么?
  9. python感叹号是什么意思_监控画面的这个感叹号是什么意思
  10. 太阳黄经和平黄经的计算方法