tpgf 发表于 2024-5-7 17:28

芯旺微 KF32A156/KF32A150系列chipon 32位机的LIN总线使用基础

hello大家好,今天主要来说KF32A156系列(包括KF32A146/KF32A136)的LIN模块的配置(这里均已A02版本为准)。
首先要说明的是:LIN是通过串口来支持的,也就是串口能够发出支持lin协议的相关操作。

至于lin帧的格式和协议,这个网上其他资料较多,此处不作详细说明,可参考下图:



这里主要说一下lin帧的组成:间隔场 - 同步场 - ID场 - 数据场 - 校验场

主机任务:间隔场 - 同步场 - ID场 - 数据场 - 校验场 ;间隔场 - 同步场 - ID场 。
从机任务:数据场 - 校验场 。

lin任务:主机任务 , 从机任务

lin主机配置
主机:可执行 主机任务 和 从机任务
配置外设的三步走:IO口配置 ,usart模块配置 ,配置相关中断
这里就和官方例程保持同步,以串口5映射到为例说明

io口配置:配置io口为重映射模式,再重映射为串口,函数代码如下

void LIN_UASRT_GPIO_INIT()
{
        GPIO_Write_Mode_Bits (GPIOA_SFR,GPIO_PIN_MASK_7, GPIO_MODE_RMP);
        GPIO_Write_Mode_Bits (GPIOA_SFR,GPIO_PIN_MASK_8, GPIO_MODE_RMP);
        GPIO_Pin_RMP_Config(GPIOA_SFR, GPIO_Pin_Num_7, GPIO_RMP_AF13);
        GPIO_Pin_RMP_Config(GPIOA_SFR, GPIO_Pin_Num_8, GPIO_RMP_AF13);
}



串口配置:外设配置

void USART_LIN_config(USART_SFRmap *USARTx)
{
        USART_InitTypeDef USART_InitStructure;

        /* Reset and enable USARTx */
        USART_Reset(USARTx);
        /* configure USARTx to LIN mode */
        USART_Struct_Init(&USART_InitStructure);
    USART_InitStructure.m_Mode=USART_MODE_FULLDUPLEXASY;                     
    USART_InitStructure.m_TransferDir=USART_DIRECTION_FULL_DUPLEX;            
    USART_InitStructure.m_WordLength=USART_WORDLENGTH_8B;                     
    USART_InitStructure.m_StopBits=USART_STOPBITS_1;                           
    USART_InitStructure.m_BaudRateBRCKS=USART_CLK_HFCLK;                     
    USART_InitStructure.m_BRAutoDetect=USART_ABRDEN_OFF;
    /** Use 16M clock as an example to list the following baud rates
       *         4800    z:208    x:0    y:0
       *         9600    z:104    x:0    y:0
       *         19200   z:52   x:0    y:0
       *         115200z:8      x:1    y:13
        */
        /* Integer part z, get value range is 0 ~ 0xFFFF */
    USART_InitStructure.m_BaudRateInteger=52;
        /* Numerator part x, get value range is 0 ~ 0x0f */
    USART_InitStructure.m_BaudRateNumerator=0;
        /* Denominator part y, get value range is 0 ~ 0x0f */
    USART_InitStructure.m_BaudRateDenominator=0;
        USART_Configuration(USARTx,&USART_InitStructure);
        /* Enable receive interrupt */
        USART_RDR_INT_Enable(USARTx,TRUE);       
        /* Enable LIN moudle */
        USART_RESHD_Enable (USARTx, TRUE);                                       
        USART_Cmd(USARTx,TRUE);                                  
}



中断配置:刚刚在串口初始化的已经将接收中断开启,那么接下来要开启串口中断

        INT_Interrupt_Enable(INT_USART5, TRUE);    //开启串口5中断
        //下面两行代码可忽略,仅是中断相关配置
        INT_Interrupt_Priority_Config(INT_USART5, 7, 1);       
        /* Configure interrupt priority group, default is 3VS1 */
        INT_Priority_Group_Config(INT_PRIORITY_GROUP_3VS1);   


发送数据:注意这里发送间隔场的时候需要用到定时器来延时

//我们就用基本定时器T14用来延时,周期值可根据波特率自行配置,需要至少13个延时
TIM_Reset(T14_SFR);                        //定时器外设复位,使能外设时钟
BTIM_Work_Mode_Config(T14_SFR,BTIM_TIMER_MODE);                //定时模式选择
BTIM_Set_Counter(T14_SFR,0);                                //定时器计数值
BTIM_Set_Period(T14_SFR,21632);                                //定时器周期值
BTIM_Set_Prescaler(T14_SFR,0);                       //定时器预分频值0+1=1(不分频)
BTIM_Clock_Config(T14_SFR,BTIM_HFCLK);    //选用HFCLK时钟
BTIM_Counter_Mode_Config(T14_SFR,BTIM_COUNT_UP_OF);        //向上计数,上溢产生中断标志


//发送接口函数这里已经做好,如果是主机收的情况下,不再发送校验场
//参数:串口号;从机id;数据;数据个数
void LIN_Send(USART_SFRmap* USARTx, uint8_t SlaveID, uint8_t* Databuf, uint32_t Length)
{
        /* Calculate the protection data */
        uint8_t ProtectID = GetParityValue(SlaveID);
        /* Calculate the checksum data */
        uint8_t CheckVaule = GetCheckSumValue(ProtectID, Databuf, Length);

        USART_Send_Blank_Enable(USARTx,TRUE);                                                //使能发送间隔
        BTIM_Clear_Overflow_INT_Flag (T14_SFR);                                                //清T14溢出中断标志位
        BTIM_Set_Counter(T14_SFR,0);                                                                //定时器计数值清0
        BTIM_Cmd(T14_SFR,TRUE);                                                                                //定时器启动控制使能
        while(!BTIM_Get_Overflow_INT_Flag(T14_SFR));                                //等待定时
        USART_Send_Blank_Enable(USARTx,FALSE);                                                //清发送间隔
        BTIM_Cmd(T14_SFR,FALSE);                                                                        //定时器关闭
        USART_SendData(USARTx,0x55);                                                          //设置同步码
        /* Send Protected ID */
        USART_SendData(USARTx, ProtectID);
        /* Send user data */
        for (uint8_t i = 0; i < Length; i++)
        {
                USART_SendData(USARTx, Databuf);
        }
        /* Send checksum data */
        if(Length)
        {
                USART_SendData(USARTx, CheckVaule);
        }
}



ok,至此主机写说完,只需要调用LIN_Send函数传入所需参数即可。这里不再详细说明主机读的过程,由于开启了接收中断,故而主机读相当于串口接收数据的过程。在中断接收即可。

lin从机配置
这里同样为了保持和官方例程一致的,我们也在从机发送的过程中使用DMA传输数据
,这里不再赘述io口配置,请参考主机的部分。

**串口配置:**做为从机的话,要打开接收中断和间隔场中断,以及使能DMA传输

void USART_LIN_config(USART_SFRmap *USARTx)
{
        USART_InitTypeDef USART_InitStructure;

        /* Reset and enable USARTx */
        USART_Reset(USARTx);
        /* configure USARTx to LIN mode */
        USART_Struct_Init(&USART_InitStructure);
        USART_InitStructure.m_Mode = USART_MODE_FULLDUPLEXASY;
        USART_InitStructure.m_TransferDir = USART_DIRECTION_FULL_DUPLEX;
        USART_InitStructure.m_WordLength = USART_WORDLENGTH_8B;
        USART_InitStructure.m_StopBits = USART_STOPBITS_1;
        USART_InitStructure.m_BaudRateBRCKS = USART_CLK_HFCLK;
        USART_InitStructure.m_BRAutoDetect = USART_ABRDEN_OFF;
        /** Use 16M clock as an example to list the following baud rates
       *         4800    z:208    x:0    y:0
       *         9600    z:104    x:0    y:0
       *         19200   z:52   x:0    y:0
       *         115200z:8      x:1    y:13
       */
        /* Integer part z, get value range is 0 ~ 0xFFFF */
        USART_InitStructure.m_BaudRateInteger = 52;
        /* Numerator part x, get value range is 0 ~ 0x0f */
        USART_InitStructure.m_BaudRateNumerator = 0;
        /* Denominator part y, get value range is 0 ~ 0x0f */
        USART_InitStructure.m_BaudRateDenominator = 0;
        USART_Configuration(USARTx, &USART_InitStructure);
        /* Enable receive interrupt */
        USART_RDR_INT_Enable(USARTx, TRUE);
        /* Enable LIN moudle */
        USART_RESHD_Enable(USARTx, TRUE);
        /* Enable LIN break interrupt */
        USART_Blank_INT_Enable(USARTx, TRUE);
        USART_Cmd(USARTx, TRUE);

        USART_DMA_Read_Receive_Enable(USARTx, TRUE);
        USART_DMA_Write_Transmit_Enable(USARTx, TRUE);
}



配置发送DMA配置

#define LIN_DATA_LEN_8_BYTE (1 + 1 + 8 + 1) /*SynchField+PID+Data+CheckSum*/

void Usart_Dma_Init(USART_SFRmap *USARTx)
{
        /* Reset the DMA0 peripheral to enable the peripheral clock */
        DMA_Reset(DMA0_SFR);
        /*
       * DMA0_TX configured as follow:
       *   - DMA channel selection channel 1
       *   - Bit width of memory = 8
       *   - Cyclic mode disable
       */
        DMA_InitTypeDef DMA_TX_INIT, DMA_RX_INIT;
        DMA_TX_INIT.m_Channel = DMA_CHANNEL_2;
        DMA_TX_INIT.m_Direction = DMA_MEMORY_TO_PERIPHERAL;
        DMA_TX_INIT.m_PeripheralDataSize = DMA_DATA_WIDTH_8_BITS;
        DMA_TX_INIT.m_MemoryDataSize = DMA_DATA_WIDTH_8_BITS;
        DMA_TX_INIT.m_Priority = DMA_CHANNEL_LOWER;
        DMA_TX_INIT.m_Number = LIN_DATA_LEN_8_BYTE;
        DMA_TX_INIT.m_PeripheralInc = FALSE;
        DMA_TX_INIT.m_MemoryInc = TRUE;
        DMA_TX_INIT.m_LoopMode = FALSE;
        DMA_TX_INIT.m_BlockMode = DMA_TRANSFER_BYTE;
        DMA_TX_INIT.m_MemoryAddr = (uint32_t)0;
        DMA_TX_INIT.m_PeriphAddr = (uint32_t) & (USARTx->TBUFR);
        DMA_Configuration(DMA0_SFR, &DMA_TX_INIT);
        /*
       * DMA0_RX configured as follow:
       *   - DMA channel selection channel 2
       *   - Bit width of memory = 8
       *   - Number of data transmitted = 100
       *   - Cyclic mode disable
       */
        DMA_RX_INIT.m_Channel = DMA_CHANNEL_3;
        DMA_RX_INIT.m_Direction = DMA_PERIPHERAL_TO_MEMORY;
        DMA_RX_INIT.m_PeripheralDataSize = DMA_DATA_WIDTH_8_BITS;
        DMA_RX_INIT.m_MemoryDataSize = DMA_DATA_WIDTH_8_BITS;
        DMA_RX_INIT.m_Priority = DMA_CHANNEL_LOWER;
        DMA_RX_INIT.m_Number = LIN_DATA_LEN_8_BYTE + 1;
        DMA_RX_INIT.m_PeripheralInc = FALSE;
        DMA_RX_INIT.m_MemoryInc = TRUE;
        DMA_RX_INIT.m_LoopMode = TRUE;
        DMA_RX_INIT.m_BlockMode = DMA_TRANSFER_BYTE;
        DMA_RX_INIT.m_PeriphAddr = (uint32_t) & (USARTx->RBUFR);
        DMA_RX_INIT.m_MemoryAddr = (uint32_t)LinRxFrame.Data;
        DMA_Configuration(DMA0_SFR, &DMA_RX_INIT);
}




中断处理:

void __attribute__((interrupt)) _USART5_exception(void)
{
        volatile uint8_t Rev_Temp;
        if (USART_Get_Blank_Flag(USART5_SFR))
        {
                /* 清除间隔场中断标志位 */
                USART_Clear_Blank_INT_Flag(USART5_SFR);
               
                /* 重置接收状态 */
                //user cod
        }
        if (USART_Get_Receive_BUFR_Ready_Flag(USART5_SFR))
        {
                /* 清除接收中断标志位 */
                USART_Clear_Receive_BUFR_INT_Flag(USART5_SFR);
                /* 读取数据 */
                val = USART_ReceiveData(USART5_SFR);
       
          //usercod
        }
}



这样配置ok后,在中断判断完间隔场中断后,进行ID判断,如果ID是从机所需id,那么即可进行相应的发送或者接收操作,此过程和串口接收完全类似,不同的是,需要注意检验场的发送不要遗漏。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/Fanshijun1/article/details/129405950

weifeng90 发表于 2024-5-7 21:19

LIN主要还是车载电子通信用的多。

chenqianqian 发表于 2024-5-8 08:15

LIN主要还是车载应用,其他应用场景很少见。
页: [1]
查看完整版本: 芯旺微 KF32A156/KF32A150系列chipon 32位机的LIN总线使用基础