SAE J1850 汽车总线协议 VPW 物理层驱动程序在STM32芯片上的实现
原文地址::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芯片上的实现相关推荐
- 空调控制协议盒_睿志诚汽车总线协议盒 后装车机也能控制车辆
车内加装了导航车机后,我们会发现通过车机也能控制车辆空调.车灯.门窗等信息,那么到底车机是通过什么实现的呢?据悉,只需要在车机内接入睿志诚Can-Bus汽车总线协议盒,这些功能就能"信手拈来 ...
- gprs模块http mqtt_在GPRS模块(SIM800C)和STM32芯片上实现MQTT协议 | TsonTec:测量解决方案提供者...
最近真是的好一个劲的折腾,算是完全搞明白了如何在STM32上实现MQTT协议了. 目录 [显示] 一.本教程中说明的内容 先说说本文化的适用范围吧: 一.使用的芯片是STM32F103C8T6,但是并 ...
- POWERLINK协议源码(最新)在stm32单片机上的移植指南
最近着了powerlink的道,连续几晚十二点前没睡过觉.不得不说兴趣这东西劲太大了,让人睡不着.喜欢上研究POWERLINK,最新版的源码结构挺清晰的,移植并测试了嵌入式linux作为从站和电脑主站 ...
- 细说汽车电子通信总线之CAN 2.0 总线协议详解
引言 1. CAN总线发展历史与ISO规范 2. CAN总线主要功能特性 3. CAN 2.0总线协议的物理层电气特性 4. CAN 2.0总线协议消息报文详解 4.1 CAN2.0总线的通信报文帧格 ...
- 细说汽车电子通信总线之LIN总线协议详解
内容提要 1. LIN总线概述 1.1 LIN总线特点及发展历史 1.2 LIN 总线协议的物理层(收发器) 2. LIN总线报文帧结构(Message Frame Structure)详解 2.1 ...
- can总线不加末端电阻_细说汽车电子通信总线之CAN 2.0 总线协议详解
引言 1. CAN总线发展历史与ISO规范 2. CAN总线主要功能特性 3. CAN 2.0总线协议的物理层电气特性 4. CAN 2.0总线协议消息报文详解 4.1 CAN2.0总线的通信报文帧格 ...
- CAN总线协议(一)
1.1 CAN 协议简介 CAN 是控制器局域网络 (Controller Area Network) 的简称,它是由研发和生产汽车电子产品著称的德国 BOSCH 公司开发的,并最终成为国际标准(IS ...
- pci总线定时协议_汽车总线测试的“解忧杂货店”
"我的回答之所以发挥了作用,原因不是别的,是因为大家自己很努力." -- 东野圭吾<解忧杂货店> 相信很多读者都看过东野圭吾的书<解忧杂货店>或者同名电影, ...
- 汽车诊断协议,(K线/CAN总线、kwp2000、ISO14230、ISO1575...)
相信像我这样的小白第一次接触汽车诊断协议肯定有点懵逼,什么鬼kwp2000,那什么又是ISO-14230,ISO-15765,ISO-14229,UDS,UDSonCAN???它们到底是什么关系,还有 ...
最新文章
- WinCE5.0中应用程序如何直接写屏
- 人民日报智慧媒体研究院与第四范式合资成立智媒新创 赋能智慧媒体创新
- Detectron:Pytorch-Caffe2-Detectron的一些跟进
- java 对象克隆_JAVA对象克隆
- rmlv出错处理一例
- lua代码格式化工具_lua的代码覆盖率工具
- 阿里巴巴 CTO 首次分享技术战略
- html5查询通配符,通配符有哪些?
- HTML5 实例:旋转同时放大缩小的方块(canvas)
- 利用java图形用户界面实现后台管理系统
- Java-常用实现分页查询
- Java开发Mye_Java使用MyEclipse构建webService简单案例
- 数据库分区:MySQL分区
- ROS(ERROR):Rosdep cannot find all required resources to answer your query
- 2016版excel_Python使用openpyxl和pandas处理Excel文件实现数据脱敏案例一则
- Android 打包后apk安装失败
- Pytorch 构建简单Neural Networks
- keil c语言字符串赋值,keil c指针变量赋值 请指点下迷津
- DVDStyler(dvd制作工具)官方中文版V3.2 | 制作dvd视频光盘软件下载 | 怎么制作dvd视频光盘
- 已经三十多岁了还是一事无成,还能靠做自媒体翻身吗?
热门文章
- vivox21支持html,vivo X21支持双卡吗_vivo X21支持双卡双待吗-太平洋IT百科
- linux opencv 显示视频文件夹,opencv cvCaptureFromFile读取视频问题汇总
- 计算机外观保养的建议,网际电脑如何建议客户您怎样保养电脑
- TIM微秒级计时学习笔记
- 利用百度地图采集大量某一区域经纬度信息
- 无法访问计算机无效的语法,您输入的表达式包含无效语法(The expression you entered contains invalid syntax)...
- 泰课在线android,泰课在线rollaball
- 什么是接口,面向接口编程的好处是什么?
- python感叹号是什么意思_监控画面的这个感叹号是什么意思
- 太阳黄经和平黄经的计算方法