[LKS32 硬件]

【LKS32AT085评测】2.电动自行车一线通应用之SIF

[复制链接]
1726|13
手机看帖
扫描二维码
随时随地手机跟帖
yang377156216|  楼主 | 2022-9-19 15:44 | 显示全部楼层 |阅读模式
本帖最后由 yang377156216 于 2022-9-19 15:47 编辑
#申请原创# @21小跑堂

前言
前面说到可以通过某个专业领域应用去打开市场,这里想推荐一个领域:两轮电动自行车。这个东西相信大家都非常熟悉,几乎每天都要与它打交道,属于家家必备的一大件了。就我所知,目前一辆两轮车上面要用到主控芯片的有这几大电子设备:电机电控器、锂电池 BMS、蓄电池充电器、液晶仪表盘、智能轮毂锁、防盗报警器以及高端点的还带中央物联网数采模块。此外还有周边衍生出来的一些产品:智能交流/直流充电桩、换电柜以及专业的新能源检测设备等等。可以看到这个领域对 MCU 的需求量是巨大的,尽管与汽车电子的要求差距很大,但我相信发展到最后,这个领域的所有电子产品也需要起码达到工业级的标准了,而不会再像现在这么 Low。之前与行业内的客户工程师聊天,据说雅迪无锡研发就一直有在招聘比亚迪的汽车应用开发工程师去搞两轮车的应用,卷到极致了吧?!那正好说明这条赛道的生意机会多多呀。
新国标两轮车核心框架.jpeg

两轮电动车行业情况
根据中国自行车协会数据显示,2021年我国电动两轮车保有量已经达到3.4亿辆,电动车累计销量达成4100万辆。按照当前电动两轮车的 3~5 年自然更替的周期来看,这是一个市场规模千亿级、且稳定发展的产业。
有专家预计,2022年中国两轮电动车保有量将达到3.5亿辆。且市场需求仍将保持增长态势,未来三年,电动两轮车替换量有望达到8000万辆,整体需求量约1.8亿辆,年均销售量超过6000万辆。
2021年两轮电动车市场集中度进一步提高,品牌生存竞争加剧,以雅迪为代表的传统品牌企业通过高端化、智能化产品路线提升品牌价值,以九号、小牛为代表的新势力品牌大举铺设渠道扩张份额。2021年两轮电动车产品智能化水平明显提升,未来必将还出现新势力品牌的鲶鱼效应。
进入2022年,随着大量城市电动车新国标过渡期结束,两轮电动车换购又将迎来一轮小高峰;此外,在节能减排、碳达峰、绿色出行等政策指引及产业链上下游发展助推下,两轮电动车市场依旧拥有较大的增长潜力。在高质量发展的时代背景驱动及消费升级需求推动下,两轮电动车产品向高品质、智能化、个性化发展。电池续航、性能进一步提升,车型设计更加符合细分用户骑行场景需要;智能化技术应用水平提升,使用体验升级。在智能化发展趋势下,两轮电动车的市场竞争将逐渐导向产品智能化竞争以及基于智能两轮电动车的服务生态的竞争。
不仅国内保有量以及销量数据在逐年递增,在国际市场出口数量和金额更是呈大幅上升趋势,以下两图分别展示了这两组数据:
国内电单车市场数据.jpg
两轮电动自行车出口情况.png
关于更多的行业分析,可以参见我附件内容中的两份调研报告:
艾瑞咨询联合鲁大师发布的一则研究报告--《2022年中国两轮电动车行业白皮书》
电子发烧友记者发布的一则新闻--《两轮电动车市场需求广阔》

电动自行车通信协议团体标准
电动自行车安全问题关系人民群众生命财产安全、关系社会和谐稳定,强化电动自行车先期治理既是保障群众生命财产安全的重要举措,也是目前推动产业高质量发展、抢占市场份额的重要抓手。为了更规范化的管理电动自行车生产、销售与安全使用,需要有一些规范和标准在符合民生国情的基础上颁布出来。
众所周知,无锡是全国最大的电动车生产基地之一,集中了新日、雅迪、爱玛、台铃、小刀等头部品牌,带动了江苏及本地近300多家整车、配件企业为全国贡献了三分之一多的产销量,无锡电动车产业的发展态势,是全国电动车行业发展的风向标。同时,无锡市已有电动车销售门店800多家,备案销售品牌724家,电动车社会保有量已超350多万辆,雅迪、新日、爱玛、台铃、小刀、新蕾、欧派、新大洲、金箭、小鸟、格林豪泰、捷安特、小牛、九号、绿源、绿佳、立马等知名品牌在无锡均有销售。由此可见,无锡市电动车的生产、销售、上牌、上路行驶、市场监管等工作尤为重要。
4月25日,江苏省自行车电动车协会根据《江苏省自行车电动车协会团体标准管理办法》有关规定,正式批准发布《电动自行车通信协议》团体标准,标准编号为:T/JSEBA 002—2022,该标准将于今年10月25日起正式实施。此次《电动自行车通信协议》团体标准制修订工作,由无锡市市场监管局、市工信局指导,江苏省自行车电动车协会牵头,联合主要生产厂家、认证机构、质检部门、科研院所等单位组成起草小组,历时4个多月,进行了多轮认真讨论与修订,并采纳了社会与行业专家意见后完成。这份标准是极具历史意义和代表性质的。
最终形成的通讯协议团体标准中主要涉及到了常用的通讯协议,分别为一线通、RS485 和 CAN , 完整版的文档我也搜索到了,一并放在附件里给大家参阅。
团体标准.png

一线通 SIF 协议
下面要讲今天的主角了——一线通 SIF 协议。在早些年间很多 MCU 都没有标准的串行 UART 通讯口,更加别说带 IIC、SPI 等接口了,外围硬件通讯接口资源严重匮乏,在需要和其它设备或者器件进行简单的通讯时(速度要求不高,硬件上只能提供一根通讯线),可以使用 SIF 协议来完成数据交互。什么是一线通 SIF 呢?以下框图示意了一个连接应用场景:
一线通总线框架图.png
SIF 协议满足以下一些特点:
  • 一线通 SIF 协议比较简单,实现起来成本较低,适用于一些数据量不大且通讯品质要求不那么高的场景;
  • 属于单线主从单工通讯模式,类似 1-Wire 协议但又与之有很大不同,为单工通讯的,即设备要么仅作为发送端,要么仅作为接收方,比如上面框图中 BMS 为发送发(主机),控制器和充电器仅为接收方(从机);
  • 波特率要么像 UART 一样提前固定好,要么让接收端自动适应发送方的同步信号从而来解析出匹配到的波特率;
  • 一次传输一帧数据,每帧数据由 同步信号(发送主报文的前导信号) + 数据信号(8bit * 12个数据位的有效数据内容,按一定占空比进行发送)+ 结束信号(一帧完整的数据发送结束的标志信号) 3个部分组成,传输结束后要求线路空闲状态为低电平,每次传输需一次性完整传输所有数据;
  • 数据的电平遵守 TTL 规范。

CMOS_TTL .png

一般一线通接口原理设计如下,IN 为主机单片机的输出口,OUT 为总线接口:
SIF 总线接收电路.png
可以看到主机的输出口是为下拉的,一般 IO 内部自带下拉也可以外部增加下拉电阻,而接收方的从机默认是需要被上拉的,上拉电阻值一般要求为 5V上拉 2.2K,3.3V 上拉 1K。
SIF 逻辑电平信号定义如下:
SIF 信号定义.png
SIF 停止信号.png
关于信号定义还有如下一些注意点:
  • 32Tosc 范围为 0.5ms,考虑到响应速度,不要超过1ms
  • 同步信号:>992Tosc 的低电平 + 32Tosc 的高电平,空闲位时间>15ms(0.5ms*992/32=15ms)
  • 数据位逻辑 1 符合 高电平时间 > 低电平时间 + 0.5ms;
  • 数据位逻辑 0 符合 低电平时间 > 高电平时间 + 0.5ms;
  • 高低电平的比例一般使用 0.5ms 和 1ms 的比例,及占空比为 1:2 (适宜取 75%)

凌鸥 SIF 接口驱动代码
如果不用硬件 SIF 接口而用定时器中断+IO 模拟,那需要将定时器更新时长设置为一个 Tosc,在中断处理函数中判断是否有数据需要发送,调用如下发送数据的处理函数:
#define SIF_VERSION 1
#define SIF_SYNC  53
#define SIF_SEND_COUNT 3

volatile static uint8_t sif_sync_tosc  = 0;
volatile static uint8_t sif_send_tosc = 0;
volatile static uint8_t state_mode = 0;
volatile static int8_t bit_cnt = 7;
volatile static uint8_t byte_cnt = 0;

static uint8_t result[64] = {0};//需要发送的数据
volatile static uint8_t length = 0;//数据的长度

static void sif_send_data_handle(uint8_t state)
{

    switch(state)
    {
        case SYNC_SIGNAL://同步模式
            if (sif_sync_tosc < SIF_SYNC - 2) {
                sif_turn_off();
            } else {
                sif_turn_on();
            }
            sif_sync_tosc++;
            if (sif_sync_tosc >= SIF_SYNC)
            {
                state_mode = SEND_DATA;
                sif_sync_tosc = 0;
                bit_cnt = 7;
                byte_cnt = 0;
                sif_send_tosc = 0;
            }
            break;
        case SEND_DATA:    //发送数据
            static uint8_t res;            
            uint8_t count = SIF_SEND_COUNT;
            static uint8_t nums = sizeof(uint8_t) * 8;
            uint8_t *p = (uint8_t *)result;
            sif_send_tosc = sif_send_tosc % count;

            uint8_t data = (p[byte_cnt] >> bit_cnt) & 0x1;
            
            if (data)
            {
                if (sif_send_tosc == 0)
                {
                    sif_turn_off();
                    sif_send_tosc ++;
                }
                else if (sif_send_tosc == 1)
                {
                    sif_turn_on();
                    sif_send_tosc ++;
                } else {
                    sif_send_tosc = 0;
                }
            }
            else
            {
                if (sif_send_tosc == 0)
                {
                    sif_turn_off();
                    sif_send_tosc++;
                }
                else if (sif_send_tosc == 2)
                {
                    sif_turn_on();
                    sif_send_tosc = 0;
                } else
                {
                    sif_send_tosc++;
                }
            }
            if (sif_send_tosc == 0)
            {
                if (--bit_cnt < 0)
                {
                    byte_cnt++;
                    bit_cnt = 7;
                }
                if (byte_cnt >= length)
                {
                    state_mode = SEND_DATA_COMPLETE;
                    break;
                }
            }
            break;
        case SEND_DATA_COMPLETE://数据发送完成,将标志位清0
            state_mode = 0;
            sif_turn_off();
            length = 0;
            memset(result, 0, sizeof(result));
            break;
        default:
            break;
        }
}
可以发现模拟的方式发送 SIF 数据帧还是非常复杂的,下面来看看使用凌鸥的 SIF 接口如何实现发送数据。由于官方SDK 开发包以及其它参考资料中都没有提及该接口如何使用,底层驱动函数库中也还没有将其做好来直接给用户使用,所以得根据 UM 手册和参照 UART 的抽象层驱动写法自己去实现。
下面为 SIF 外设驱动程序 lks32at08x_sif.c 和 lks32at08x_sif.h :
/*******************************************************************************
* 版权所有 (C)2015, LINKO SEMICONDUCTOR Co.ltd
*
* 文件名称: lks32at08x_sif.c
* 文件标识:
* 内容摘要: SIF 外设驱动程序
* 其它说明: 无
* 当前版本: V 1.0
* 作    者: yang377156216
* 完成日期:
*
* 修改记录1:
* 修改日期:
* 版 本 号:V 1.0
* 修 改 人:
* 修改内容:创建
*
******************************************************************************/
#include "lks32at08x_sif.h"


/*******************************************************************************
函数名称:    void SIF_Init(SIF_TypeDef *SIFx, SIF_InitTypeDef *SIF_InitStruct)
功能描述:    SIF 初始化
操作的表:    无
输入参数:    SIF_TypeDef *SIFx, SIF_InitTypeDef *SIF_InitStruct
输出参数:    无
返 回 值:    无
其它说明:
修改日期      版本号          修改人            修改内容
-------------------------------------------------------------------------------
2022/08/19      V1.0          yang377156216    创建
******************************************************************************/
void SIF_Init(SIF_TypeDef *SIFx, SIF_InitTypeDef *SIF_InitStruct)
{
    if (SIFx == SIF0)
    {
        //开启 SIF 的时钟,这里默认就是开启的;
    }
   
    SIFx ->CFG &= ~(0x01 << 1);
    SIFx ->CFG |= (SIF_InitStruct->Endian << 1);
   
    SIFx ->FREQ &= ~(0x0F << 4);
    SIFx ->FREQ |= (SIF_InitStruct->Tosc << 4);
   
    SIFx ->FREQ &= ~(0x01 << 0);
    SIFx ->FREQ |= (SIF_InitStruct->Duty << 0);
   
    SIFx ->IRQ &= ~(0x01 << 0);
    SIFx ->IRQ |= (SIF_InitStruct->TxIE << 0);
}

/*******************************************************************************
函数名称:    void SIF_DeInit(SIF_TypeDef *SIFx)
功能描述:    SIF 反初始化
操作的表:    无
输入参数:    SIF_TypeDef *SIFx
输出参数:    无
返 回 值:    无
其它说明:
修改日期      版本号          修改人            修改内容
-------------------------------------------------------------------------------
2022/08/19      V1.0          yang377156216    创建
******************************************************************************/
void SIF_DeInit(SIF_TypeDef *SIFx)
{
    SIFx ->CFG = 1;
    SIFx ->FREQ = 0;
    SIFx ->IRQ = 0;
    SIFx ->WDATA = 0;
    SIFx ->CFG = 0;
}

/*******************************************************************************
函数名称:    void SIF_StructInit(SIF_InitTypeDef *SIF_InitStruct)
功能描述:    SIF 结构体初始化
操作的表:    无
输入参数:    SIF_InitTypeDef *SIF_InitStruct
输出参数:    无
返 回 值:    无
其它说明:
修改日期      版本号          修改人            修改内容
-------------------------------------------------------------------------------
2022/08/19      V1.0          yang377156216    创建
******************************************************************************/
void SIF_StructInit(SIF_InitTypeDef *SIF_InitStruct)
{
    SIF_InitStruct->Duty = SIF_DUTY_2_1;
    SIF_InitStruct->Endian = SIF_ENDIAN_LSB;
    SIF_InitStruct->Tosc = SIF_Tosc_32US_Default;
    SIF_InitStruct->TxIE = SIF_TX_IE_DIS;
}

/*******************************************************************************
函数名称:    void SIF_MouduleEnable(SIF_TypeDef *SIFx, uint8_t enable)
功能描述:    SIF 模块使能/关闭
操作的表:    无
输入参数:    SIF_TypeDef *SIFx, uint8_t enable
输出参数:    无
返 回 值:    无
其它说明:
修改日期      版本号          修改人           修改内容
-------------------------------------------------------------------------------
2022/08/19      V1.0          yang377156216    创建
******************************************************************************/
void SIF_MouduleEnable(SIF_TypeDef *SIFx, uint8_t enable)
{
    if(enable == ENABLE)
    {
        SIFx ->CFG |= SIF_MODULE_EN;
    }
    else
    {
        SIFx ->CFG &= ~SIF_MODULE_EN;
    }
}

/*******************************************************************************
函数名称:    void SIF_LastByte_Set(SIF_TypeDef *SIFx, uint8_t enable)
功能描述:    SIF 结束本次传输
操作的表:    无
输入参数:    SIF_TypeDef *SIFx, uint8_t enable
输出参数:    无
返 回 值:    无
其它说明:
修改日期      版本号          修改人           修改内容
-------------------------------------------------------------------------------
2022/08/19      V1.0          yang377156216    创建
******************************************************************************/
void SIF_LastByte_Set(SIF_TypeDef *SIFx, uint8_t enable)
{
    if(enable == ENABLE)
    {
        SIFx ->CFG |= SIF_LAST_BYTE_EN;
    }
}

/*******************************************************************************
函数名称:    void SIF_SendData(SIF_TypeDef *SIFx, uint8_t data)
功能描述:    SIF 发送数据
操作的表:    无
输入参数:    SIF_TypeDef *SIFx, uint8_t data
输出参数:    无
返 回 值:    无
其它说明:
修改日期      版本号          修改人            修改内容
-------------------------------------------------------------------------------
2022/08/19      V1.0          yang377156216     创建
******************************************************************************/
void SIF_SendData(SIF_TypeDef *SIFx, uint8_t data)
{
    SIFx->WDATA = data;
}

/*******************************************************************************
函数名称:    uint32_t SIF_GetIRQFlag(SIF_TypeDef *SIFx, uint32_t tempFlag)
功能描述:    取得 SIF 中断标志
操作的表:    无
输入参数:    SIF_TypeDef *SIFx, uint32_t tempFlag
输出参数:    无
返 回 值:    SIF 中断标志
其它说明:
修改日期      版本号          修改人            修改内容
-------------------------------------------------------------------------------
2022/08/19      V1.0          yang377156216     创建
******************************************************************************/
uint32_t SIF_GetIRQFlag(SIF_TypeDef *SIFx, uint32_t tempFlag)
{
    if (((SIFx->IRQ & tempFlag) != Bit_RESET) &&                               \
                                        (SIFx->IRQ & SIF_TX_IE_EN) != Bit_RESET)
    {
        return 1;
    }
    return 0;
}

/*******************************************************************************
函数名称:    void SIF_ClearIRQFlag(SIF_TypeDef *SIFx, uint32_t tempFlag)
功能描述:    清除 SIF 中断标志位
操作的表:    无
输入参数:    SIF_TypeDef *SIFx, uint32_t tempFlag
输出参数:    无
返 回 值:    无
其它说明:
修改日期      版本号          修改人            修改内容
-------------------------------------------------------------------------------
2022/08/19      V1.0          yang377156216     创建
******************************************************************************/
void SIF_ClearIRQFlag(SIF_TypeDef *SIFx, uint32_t tempFlag)
{
    SIFx->IRQ |= tempFlag;
}

/*********************** (C) COPYRIGHT LINKO SEMICONDUCTOR *****END OF FILE****/
/*******************************************************************************
* 版权所有 (C)2015, LINKO SEMICONDUCTOR Co.ltd
*
* 文件名称: lks32at08x_sif.h
* 文件标识:
* 内容摘要: SIF 驱动头文件
* 其它说明: 无
* 当前版本: V 1.0
* 作    者: yang377156216
* 完成日期:
*
* 修改记录1:
* 修改日期:
* 版 本 号:V 1.0
* 修 改 人:
* 修改内容:创建
*
******************************************************************************/

#ifndef __LKS32AT08x_SIF_H
#define __LKS32AT08x_SIF_H

/* Includes ------------------------------------------------------------------*/
#include "lks32mc08x_lib.h"

typedef struct
{
   __IO uint32_t CFG;
   __IO uint32_t FREQ;
   __IO uint32_t IRQ;
   __IO uint32_t WDATA;
} SIF_TypeDef;


typedef enum
{
   SIF_Tosc_32US_Default  = 0x00,
   SIF_Tosc_32US  = 0x01,
   SIF_Tosc_64US  = 0x02,
   SIF_Tosc_96US  = 0x03,
   SIF_Tosc_128US = 0x04,
   SIF_Tosc_160US = 0x05,
   SIF_Tosc_192US = 0x06,
   SIF_Tosc_220US = 0x07,
   SIF_Tosc_256US = 0x08,
   SIF_Tosc_288US = 0x09,
   SIF_Tosc_320US = 0x0A,
} SIF_ToscSet;

typedef struct
{
   uint8_t Endian;             /*SIF 大小端,1 = MSB , 0 = LSB 默认*/
   uint8_t Tosc;               /*96Mhz 系统时钟下,基准时间单位 Tosc 设置*/
   uint8_t Duty;               /*SIF 传输 Data 占空比*/
   uint8_t TxIE;               /*发送中断使能*/
} SIF_InitTypeDef;

#ifndef SIF0
#define SIF0                ((SIF_TypeDef *) SIF_BASE)
#endif

#ifndef SIF_MCU_MCLK
#define SIF_MCU_MCLK        (96000000LL) /* 系统时钟 */
#endif

#define SIF_LAST_BYTE_EN    (1 << 4)     /*是否结束本次传输*/

#define SIF_ENDIAN_LSB      0            /*低端先发送*/
#define SIF_ENDIAN_MSB      1            /*高端先发送*/

#define SIF_MODULE_EN       (1 << 0)     /*SIF 模块使能*/

#define SIF_DUTY_2_1        0            /*占空比为 2:1*/
#define SIF_DUTY_3_1        1            /*占空比为 3:1*/

#define SIF_TX_IE_DIS       0            /*SIF 中断禁止*/
#define SIF_TX_IE_EN        1            /*SIF 中断使能*/


/*中断标志定义*/
#define SIF_TX_IFLAG        (1 << 4)     /*发送完成中断*/

void SIF_MouduleEnable(SIF_TypeDef *SIFx, uint8_t enable);

void SIF_LastByte_Set(SIF_TypeDef *SIFx, uint8_t enable);

void SIF_DeInit(SIF_TypeDef *SIFx);
void SIF_Init(SIF_TypeDef *SIFx, SIF_InitTypeDef *SIF_InitStruct);
void SIF_StructInit(SIF_InitTypeDef *SIF_InitStruct);

void SIF_SendData(SIF_TypeDef *SIFx, uint8_t data);

uint32_t SIF_GetIRQFlag(SIF_TypeDef *SIFx, uint32_t tempFlag);
void SIF_ClearIRQFlag(SIF_TypeDef *SIFx, uint32_t tempFlag);

#endif /*__lks32mc08x_SIF_H */

/************************ (C) COPYRIGHT LINKO SEMICONDUCTOR *****END OF FILE****/
驱动代码写好后编写了测试的发送代码,只需要调用 SIF_SendData 这个接口即可实现发送 SIF 帧数据,另外还更改了不同的占空比和 Tosc 时间进行测试,验证了底层驱动的正确性,将驱动添加进了 lib 包中:
bsp lib 修改建议.png
整个测试工程的 keil 目录如下:
SIF keil 工程目录.png
下面是抓取的逻辑图:
SIF 发送4个数据 MSB模式.png SIF 发送4个数据 LSB模式.png
该接口只能做到硬件的 SIF 发送,而要做接收的话还得用传统的 定时器 + 一个GPIO口模拟方式进行采样解析,接收解析代码参考之前其它平台使用过的成功示例:
/*******************************************************************************
*@时  间 : 2022-2-22
*@摘  要 : 主程序文件
*@芯  片 :
*        
*******************************************************************************/

/*================================= Demo说明 ===================================
定时器 + 一个GPIO口进行通讯数据读取
==============================================================================*/

/* 包含的头文件 ---------------------------------------------------------------*/
//与实际单片机相关
/* 宏定义 ---------------------------------------------------------------------*/
#define DATA_REV_PIN            gpio_input_bit_get(GPIOB,BIT(9))     //定义数据接收引脚(根据实际项目进行更改)

#define LOW                     0       //低电平
#define HIGH                    1       //高电平

#define SYNC_L_TIME_NUM         1100    //同步信号低电平时间:50ms = 50000us / 50us = 1000
#define SYNC_H_TIME_NUM_MIN     8      //同步信号高电平最小时间:500-100us = 400us / 50us = 8  
#define SYNC_H_TIME_NUM_MAX     12     //同步信号高电平最大时间:500+100us = 600us / 50us = 12

#define SHORT_TIME_NUM_MIN      9     //一个逻辑周期中短的时间最小值:500-50us = 450us / 50us = 9
#define SHORT_TIME_NUM_MAX      11    //一个逻辑周期中短的时间最大值:500+50us = 550us / 50us = 11

#define LONG_TIME_NUM_MIN       18    //一个逻辑周期中长的时间最小值:1ms-100us = 900us / 50us = 18
#define LONG_TIME_NUM_MAX       22    //一个逻辑周期中长的时间最大值:1ms+100us = 1100us / 50us = 22

#define LOGIC_CYCLE_NUM_MIN     26    //一个逻辑周期最小时间:1.5ms-200us = 1300us / 50us = 26
#define LOGIC_CYCLE_NUM_MAX     34    //一个逻辑周期最大时间:1.5ms+200us = 1700us / 50us = 34

#define HALF_LOGIC_CYCLE_MIN    13    //一个逻辑周期的1/2最小时间:750-100us = 650us / 50us = 13
#define HALF_LOGIC_CYCLE_MAX    17    //一个逻辑周期的1/2最大时间:750+100us = 850us / 50us = 17

#define END_SIGNAL_TIME_NUM     100   //结束信号电平时间:5ms低电平 + Nms高电平,实际检测5ms低电平就行,一帧数据发送完成后检测5ms低电平就代表完成了,不发数据的时候上拉电阻拉高了

#define REV_BIT_NUM             8     //接收的bit位个数,看是按字节接收还是按字接收,1字节=8bit,1字=2字节=16bit
#define REV_DATA_NUM            12    //接收的数据个数


/* 类型定义 -------------------------------------------------------------------*/
typedef enum
{
    INITIAL_STATE=0,            //初始状态,等待接收同步信号
    SYNC_L_STATE=1,             //接收同步低电平信号状态
    SYNC_H_STATE=2,             //接收同步高电平信号状态
    DATA_REV_STATE=3,           //读取数据码电平状态
    END_SIGNAL_STATE=4,         //接收结束电平信号状态
    RESTART_REV_STATE=5         //接收过程出错重新接收状态
}REV_STATE_e;                   //接收数据状态枚举

/* 变量定义 -------------------------------------------------------------------*/
unsigned char receive_state=0;      //接收数据状态
unsigned char receive_bit_num=0;    //接收的bit位个数
unsigned char receive_data_num=0;   //接收的数据个数

//接收数据缓存数组-用一个数组来缓存数据,51个数据字节
unsigned char receive_data_buf[REV_DATA_NUM]={0};
unsigned char receive_data[REV_DATA_NUM] = {0};

unsigned int  H_L_Level_time_cnt=0; //高低电平时间计数

uint8_t start_H_L_Level_timming_flag=0; //开始高低电平计时标记
uint8_t has_read_bit = 0;               //1-已经读取一个bit位
uint8_t check_OK = 0;                   //1-校验和正确,0-校验和失败
uint8_t read_success=0;                 //一帧数据是否读取成功,0-不成功,1-成功
uint8_t Pin_Old = 0;
uint8_t Pin_New = 0;
uint8_t Pin_Change_Flag = 0;
uint8_t BitFinish_Flag = 0;

/* 函数声明 -------------------------------------------------------------------*/
void GPIO_Init(void);               //GPIO初始化函数
void Timer1_Init(void);             //定时器1初始化函数
void Receive_Data_Handle(void);     //接收数据处理
void Check_Sum_Handle(void);        //校验和处理

/* 函数定义 -------------------------------------------------------------------*/
/*******************************************************************************
*函数名称 : SIF_Handle
*函数功能 : SIF 解析函数,在主循环中调用
*输入参数 : void
*输出返回 : void
*******************************************************************************/
void main(void)
{
    GPIO_Init();        //GPIO初始化,设置数据接收引脚P10为浮空输入,检测高低电平
    Timer1_Init();      //定时器1初始化,定时周期为:5微秒

    while(1)
    {
        SIF_Handle();
    }
}

/*******************************************************************************
*函数名称 : SIF_Handle
*函数功能 : SIF 解析函数,在主循环中调用
*输入参数 : void
*输出返回 : void
*******************************************************************************/
void SIF_Handle(void)
{
    if (read_success == 1)              //如果成功读取一帧数据
    {
        //一帧数据接收成功后先根据协议要求进行校验和,验证数据的正确性
        Check_Sum_Handle();

        //如果数据正确,根据接收的数据进行分析获取需要的内容
        if (check_OK)
        {
            memcpy(receive_data, receive_data_buf, REV_DATA_NUM);
        }

        read_success = 0;               //读取一帧数据清0
    }
   
}

/*******************************************************************************
*函数名称 : Timer1_isr
*函数功能 : 定时器1中断处理函数
*输入参数 : void
*输出返回 : void
*******************************************************************************/

void TIMER1_IRQHandler(void)
{
    if(SET == timer_interrupt_flag_get(TIMER1, TIMER_INT_CH0))
    {
        /* clear channel 0 interrupt bit */
        timer_interrupt_flag_clear(TIMER1, TIMER_INT_CH0);
        
        Pin_New = DATA_REV_PIN;
        
        if (start_H_L_Level_timming_flag==1)
        {
          H_L_Level_time_cnt++;     //高低电平维持时间计数变量
         
          Pin_Change_Flag = 0;
          BitFinish_Flag = 0;
          if(Pin_New != Pin_Old)
          {
            Pin_Change_Flag = 1;
            if(0 == Pin_New)
            {BitFinish_Flag = 1;}
          }
          Pin_Old = Pin_New;
        }   
        Receive_Data_Handle();      //接收数据处理,波特率自适应
        
        led_toggle(LED2);//测试用的 LED
    }
}
/*******************************************************************************
*函数名称 : Receive_Data_Handle
*函数功能 : 接收数据处理
*输入参数 : void
*输出返回 : void
*******************************************************************************/
void Receive_Data_Handle(void)
{
    switch (receive_state)                          //检测当前接收数据状态
    {
        case INITIAL_STATE:                         //初始状态,未接收到同步信息,进行同步判断
            if (DATA_REV_PIN == LOW)                //判断接收引脚的电平状态,当读到低电平时,开始计时
            {
                receive_bit_num = 0;                //重置bit位计数器
                receive_data_num = 0;               //重置接收数据个数
                H_L_Level_time_cnt = 0;             //高低电平计时变量清0
                start_H_L_Level_timming_flag = 1;   //开始高低电平计时
                receive_state = SYNC_L_STATE;       //进入读取同步低电平信号状态
               
                memset(receive_data_buf, 0 ,REV_DATA_NUM);
            }
            break;
        
        case SYNC_L_STATE:                          //在读取同步低电平信号期间
            if (DATA_REV_PIN == HIGH)               //同步信号低电平检测期间读到高电平
            {
                if (H_L_Level_time_cnt >= SYNC_L_TIME_NUM)//如果同步信号低电平时间>=SYNC_L_TIME_NUM
                {                                       //同步信号低电平时间要>=10ms
                    H_L_Level_time_cnt = 0;         //高低电平计时变量清0
                    receive_state = SYNC_H_STATE;   //进入读取同步信号高电平状态
                }
                else
                {
                    receive_state = RESTART_REV_STATE;      //进入重新接收状态  
                }
            }
            break;

        case SYNC_H_STATE:                          //在读取同步信号高电平期间
            if (DATA_REV_PIN == LOW)                //同步信号高电平检测期间读到低电平
            {
                //判断同步信号高电平时间是否在1ms±100us之间
                if (H_L_Level_time_cnt >= SYNC_H_TIME_NUM_MIN && H_L_Level_time_cnt <= SYNC_H_TIME_NUM_MAX)
                {
                    H_L_Level_time_cnt = 0;         //高低电平计时变量清0
                    receive_state = DATA_REV_STATE; //进入读取数据状态
                }
                else                                    
                {
                    receive_state = RESTART_REV_STATE;      //进入重新接收状态
                }
            }
            else            //如果在同步信号高电平检测期间,时间超过2ms±200us,认为超时
            {
                //判断时间是否超时 2ms±200us
                if (H_L_Level_time_cnt >= LOGIC_CYCLE_NUM_MAX)
                {
                     receive_state = RESTART_REV_STATE;      //进入重新接收状态
                }   
            }
            break;

        case DATA_REV_STATE:          //在读取数据码电平期间
            if ((has_read_bit==0) && (H_L_Level_time_cnt >= HALF_LOGIC_CYCLE_MIN && H_L_Level_time_cnt <= HALF_LOGIC_CYCLE_MAX))
            {
                receive_data_buf[receive_data_num] |= DATA_REV_PIN;
               
                has_read_bit = 1;
             }

            //如果已经读取一个bit位,且时间计数已经>=2ms±200us,说明一个逻辑周期过去了
            if ((has_read_bit==1) && (H_L_Level_time_cnt >= LOGIC_CYCLE_NUM_MIN && H_L_Level_time_cnt <= LOGIC_CYCLE_NUM_MAX))
            //if ((has_read_bit==1) && (1 == BitFinish_Flag))
            {
                H_L_Level_time_cnt = 0;             //高低电平计时变量清0
                has_read_bit = 0;                   //清0,读取下一个bit位
                receive_bit_num++;                  //接收的bit数++
               
                if (receive_bit_num == REV_BIT_NUM)   //如果一个字节8个bit位接收完成
                {
                    //receive_data[receive_data_num] = receive_data_buf[receive_data_num];
                    
                    receive_data_num++;             //接收的数据个数++
                    receive_bit_num = 0;            //接收bit位个数清0重新接收

                    if (receive_data_num == REV_DATA_NUM)   //如果数据采集完毕
                    {
                        receive_state = END_SIGNAL_STATE;   //进入接收结束低电平信号状态
                    }  
                }
                else                                //如果一个字节8个bit位还没有接收完成
                {
                    //将接收数据缓存左移一位,数据从低bit位开始接收
                    receive_data_buf[receive_data_num] = receive_data_buf[receive_data_num] >> 1;
                }
            }        
            break;

        case END_SIGNAL_STATE:                              //在接收结束信号低电平期间
            if (DATA_REV_PIN == LOW)                       
            {
                if (H_L_Level_time_cnt >= END_SIGNAL_TIME_NUM)  //如果读到低电平时间>=5ms
                {
                        read_success = 1;                   //一帧数据读取成功
                        SIF_Handle();
                        start_H_L_Level_timming_flag = 0;   //停止高低电平计时
                        H_L_Level_time_cnt = 0;             //定时器计数值清0
                           receive_state = INITIAL_STATE;      //接收状态清0  
                }  
            }
            else    //结束信号低电平检测期间一直为低
            {
                //if (H_L_Level_time_cnt >= SYNC_L_TIME_NUM)  //如果读到低电平时间>=10ms,认为超时
                {                                           //一帧数据发送完成后需要间隔50ms才发送第二帧数据,期间肯定会被拉高
                    receive_state = RESTART_REV_STATE;      //进入重新接收状态
                }
            }
            break;

        case RESTART_REV_STATE:                     //重新接收数据状态
            start_H_L_Level_timming_flag = 0;       //停止高低电平计时
            H_L_Level_time_cnt = 0;                 //定时器计数值清0
            receive_state = INITIAL_STATE;          //接收状态清0            
            break;
    }
}

/*******************************************************************************
*函数名称 : Check_Sum_Handle
*函数功能 : 校验和处理
*输入参数 : void
*输出返回 : void
*******************************************************************************/
void Check_Sum_Handle(void)
{
    unsigned char i = 0, checkByte = 0;
    unsigned long checkXor = 0;

    for ( i = 0; i < (REV_DATA_NUM ); i++)
    {
        checkXor = checkXor ^ receive_data_buf[i];
    }
   
    checkByte = (unsigned char)checkXor;

    if (checkByte == receive_data_buf[REV_DATA_NUM-1])  //校验和正确
    {
        check_OK = 1;           //标记校验成功
    }
    else
    {
        check_OK = 0;           //标记校验失败
    }
}

uint8_t * pGetSIFData(void)
{
  return receive_data;
}
到这已经完成了 LKS32AT085 芯片的 SIF 协议测试,使用的是中断发送的方式,对 LAST_BYTE 那位的使用还存在疑惑:因为我还没有关注停止信号的发送情况,那是否靠这位去发出停止信号的呢?

仪表盘液晶屏幕一线通应用
之前给新思维做仪表项目的时候买过一个带一线通接口走新思维协议的仪表液晶模块,淘宝链接如下:
https://item.taobao.com/item.htm?spm=a1z09.2.0.0.47f72e8d5ah35W&id=593789766798&_u=tckiua7c9d2
这次可以直接拿 LKS32AT085 芯片的 SIF 接口来与之通讯,控制其显示不同的数据了,协议内容我放在附件中了,具体实现代码由于涉及到机密不在这贴出,下面为效果展示图:
SIF 仪表.jpg

小结
经过一番折腾终于将 LKS32AT085 芯片自带的 SIF 通讯接口玩起来了,不再用 timer+io 的方式去模拟了,简化了大量的实现流程和代码量,着实适用于电动自行车一线通领域,以后再做两轮电动自信车电控和仪表项目会优先考虑凌鸥的专用芯片了。另外,如果一线通能做到像汽车上的 K 线一样有专门的标准去将其“车规化”,那它在其它领域中也必将会逐渐普及起来。

3. 新思维液晶显示器一线通通信协议.zip (90.12 KB)

使用特权

评论回复
上下而求索| | 2022-9-21 06:43 | 显示全部楼层
继续跟进

使用特权

评论回复
感谢楼主的精彩分析和提供的SIF库函数文件,楼主这篇文章对SIF协议与软硬件实现讲的很细致,程序备注也很清晰,很适合小白学习与快速入手。

使用特权

评论回复
match007| | 2022-10-17 20:08 | 显示全部楼层
SIF
是一种标准协议么?
这是否为专有协议呢

使用特权

评论回复
隐现619| | 2022-10-18 17:21 | 显示全部楼层
match007 发表于 2022-10-17 20:08
SIF
是一种标准协议么?
这是否为专有协议呢

SIF有标准的波形格式,低电平和高电平的表达方式是固定的,至于具体的数据协议这个每个整车厂商各不相同。需要结合你对接的整车厂来做具体的数据内容。此模块正如楼主所说大大的节省了软件代码量。而且发送的波形更加标准。

使用特权

评论回复
punan2010| | 2023-3-18 16:45 | 显示全部楼层
你好,请问下这个SIF一线通驱动可以用在LKS32MC08x上面吗?
如果可以的话,驱动调用流程什么样子?
感谢!

使用特权

评论回复
smn568| | 2023-3-23 11:36 | 显示全部楼层
本帖最后由 smn568 于 2023-3-23 14:35 编辑

感谢分享,受益匪浅

使用特权

评论回复
anny0884| | 2023-4-10 02:27 | 显示全部楼层
按理说这种单线通讯协议速率并不快~但是我用USB 10M的示波器居然抓不到波形。再有就是这种通讯协议应该也有应答机制吧~看不懂~

使用特权

评论回复
b5226095| | 2024-1-11 13:04 | 显示全部楼层
淘宝上搜一下凌鸥,发现卖家寥寥无几,价格价格昂贵毫无竞争力,厂家推广一点都不给力,和铺天盖地的GD32比起来简直就是天壤之别,估计就是典型只靠几个固定客户过日子的厂家,这种芯片资源太少,找个开发板都困难。

使用特权

评论回复
szt1993| | 2024-1-17 15:09 | 显示全部楼层
一线通 SIF 协议有哪些优点?

使用特权

评论回复
jflahdink09| | 2024-1-17 15:16 | 显示全部楼层
定位模块一般使用谁家的比较多

使用特权

评论回复
LOVEEVER| | 2024-1-17 15:27 | 显示全部楼层
一线通 SIF 协议比较简单,实现起来成本较低,适用于一些数据量不大且通讯品质要求不那么高的场景

使用特权

评论回复
中国龙芯CDX| | 2024-1-18 11:22 | 显示全部楼层
凌鸥的 SIF 接口应该非常方便

使用特权

评论回复
一线通接口原理设计如下,IN 为主机单片机的输出口,OUT 为总线接口

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

33

主题

164

帖子

10

粉丝