申明:原创扣字不易,转载请注明源出处!!!
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
* 115200 z: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[i]);
}
/* 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
* 115200 z: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);
//user cod
}
}
这样配置ok后,在中断判断完间隔场中断后,进行ID判断,如果ID是从机所需id,那么即可进行相应的发送或者接收操作,此过程和串口接收完全类似,不同的是,需要注意检验场的发送不要遗漏。
好啦,lin的配置就这些啦,本次更新时间为20230313。