以下内容为工作应用中的小笔记,有利于自己将来使用,也提供给有需要的人参考。
主要讲述的是FlexIO在串行数据收发上的应用。
本笔记基于官方SDK,https://mcuxpresso.nxp.com/zh/welcome
其中的 fsl_flexio.c fsl_flexio.h的结构体和函数进行初始化
初始化用到的两个结构体(结构体内容说明请参考fsl_flexio.h)
flexio_shifter_config_t
数据线配置结构体
flexio_timer_config_t
时钟线配置结构体
初始化用到的两个函数(结构体内容说明请参考fsl_flexio.c)
数据线配置函数:
void FLEXIO_SetShifterConfig(FLEXIO_Type *base, uint8_t index, const flexio_shifter_config_t *shifterConfig)
时钟线备注函数:
void FLEXIO_SetTimerConfig(FLEXIO_Type *base, uint8_t index, const flexio_timer_config_t *timerConfig)
/* Enable FlexIO */
CLOCK_EnableClock(s_flexioClocks[FLEXIO_GetInstance(FLEXIO1)]);//使能FlexIO时钟 必须先使能,否则后面操作无法正常进行
uint32_t ctrlReg = FLEXIO1->CTRL;
ctrlReg &= ~(FLEXIO_CTRL_DOZEN_MASK | FLEXIO_CTRL_DBGE_MASK | FLEXIO_CTRL_FASTACC_MASK | FLEXIO_CTRL_FLEXEN_MASK);
ctrlReg |= (FLEXIO_CTRL_DBGE(true) | FLEXIO_CTRL_FASTACC(false) |
FLEXIO_CTRL_FLEXEN(true) | FLEXIO_CTRL_DOZEN_MASK);
FLEXIO1->CTRL = ctrlReg;//初始化FlexIO的状态 可以不进行初始化
shifterConfig.timerSelect = 0U; //选中时钟计数器(0-3)时钟源
shifterConfig.timerPolarity = kFLEXIO_ShifterTimerPolarityOnNegitive; //时钟极性(在时钟的 上升沿/*下降沿 进行写入读取)
shifterConfig.pinConfig = kFLEXIO_PinConfigOutput; //FlexIO 类型(禁止输出/开漏输出/双向输出/*推挽输出)
shifterConfig.pinSelect = TX_PIN; //FlexIO 位号(0-31)
shifterConfig.pinPolarity = kFLEXIO_PinActiveHigh; //FlexIO 极性(*正极性/负极性)
shifterConfig.shifterMode = kFLEXIO_ShifterModeTransmit; //定义位移器工作模式(禁用/接收/*发送/匹配存储/匹配连续)
shifterConfig.parallelWidth = 0; //并行模式带宽
shifterConfig.inputSource = kFLEXIO_ShifterInputFromPin; //选择移位器的输入源 (*从引脚输入/从移位器输入)
shifterConfig.shifterStop = kFLEXIO_ShifterStopBitDisable; //移位器停止条件(*禁用/逻辑低电平/逻辑高电平)
shifterConfig.shifterStart = kFLEXIO_ShifterStartBitDisabledLoadDataOnEnable;//移位器开始条件(*禁用移位器启动位,发送器在启动时加载数据/禁用移位器启动位,发射机在第一次移位时加载数据/逻辑低电平/逻辑高电平)
FLEXIO_SetShifterConfig(FLEXIO1, 0U, &shifterConfig);//index (0-3)数据移位器源
以上初始化了一根发送数据线,在时钟的下降沿改变数据(PS:数据长度由时钟决定)
shifterConfig.timerSelect = 0U; //选中时钟计数器(0-3)时钟源
shifterConfig.timerPolarity = kFLEXIO_ShifterTimerPolarityOnPositive; //时钟极性(在时钟的 *上升沿/下降沿 进行写入读取)
shifterConfig.pinConfig = kFLEXIO_PinConfigOutputDisabled; //FlexIO 类型(禁止输出/开漏输出/*双向输出/推挽输出)
shifterConfig.pinSelect = RX_PIN; //FlexIO 位号(0-31)
shifterConfig.pinPolarity = kFLEXIO_PinActiveHigh; //FlexIO 极性(*正极性/负极性)
shifterConfig.shifterMode = kFLEXIO_ShifterModeReceive; //定义位移器工作模式(禁用/*接收/发送/匹配存储/匹配连续)
shifterConfig.parallelWidth = 0; //并行模式带宽
shifterConfig.inputSource = kFLEXIO_ShifterInputFromPin; //选择移位器的输入源 (*从引脚输入/从移位器输入)
shifterConfig.shifterStop = kFLEXIO_ShifterStopBitDisable; //移位器停止条件(*禁用/逻辑低电平/逻辑高电平)
shifterConfig.shifterStart = kFLEXIO_ShifterStartBitDisabledLoadDataOnEnable;//移位器开始条件(*禁用移位器启动位,发送器在启动时加载数据/禁用移位器启动位,发射机在第一次移位时加载数据/逻辑低电平/逻辑高电平)
FLEXIO_SetShifterConfig(FLEXIO1, 0U, &shifterConfig);//index (0-3)数据移位器源
以上初始化了一根接收数据线,在时钟的下降沿读取数据(PS:在时钟上升沿改变数据在下降沿读取数据,所以发送线和接收线时钟极性刚好相反)
timerConfig.triggerSelect = FLEXIO_TIMER_TRIGGER_SEL_SHIFTnSTAT(0U);//触发源选择,即满足触发条件定时器开始工作(Pin/*移位寄存器/定时器)
timerConfig.triggerPolarity = kFLEXIO_TimerTriggerPolarityActiveLow; //触发极性(高/*低)
timerConfig.triggerSource = kFLEXIO_TimerTriggerSourceInternal; //触发源 (*内部/外部)
timerConfig.pinConfig = kFLEXIO_PinConfigOutput; //FlexIO 类型(禁止输出/开漏输出/双向输出/*推挽输出)
timerConfig.pinSelect = SCK_PIN; //FlexIO 位号(0-31)
timerConfig.pinPolarity = kFLEXIO_PinActiveHigh; //FlexIO 极性(*正极性/负极性)
timerConfig.timerMode = kFLEXIO_TimerModeDual8BitBaudBit; //定时器工作模式(禁用/*双8位波特模式/双八位PWM模式/单16位模式)
timerConfig.timerOutput = kFLEXIO_TimerOutputZeroNotAffectedByReset; //定时器初始状态(逻辑1,并且不受计时器重置的影响/*逻辑0,并且不受计时器重置的影响/在启动和重置时为逻辑1/在启动和重置时为逻辑0)
timerConfig.timerDecrement = kFLEXIO_TimerDecSrcOnFlexIOClockShiftTimerOutput;//配置定时器递减源(*FlexIO时钟,移位时钟等于计时器输出/触发输入,移位时钟等于计时器输出/Pin输入,移位时钟等于管脚输入/触发输入,移位时钟等于触发器输入)
timerConfig.timerReset = kFLEXIO_TimerResetNever; //定时器复位条件(*禁用/定时器引脚等于定时器输出时/定时器触发器等于定时器输出时/定时器Pin上升沿/触发源上升沿/触发上升或下降缘)
timerConfig.timerDisable = kFLEXIO_TimerDisableOnTimerCompare; //定时器失能条件(禁用/定时器N-1禁用/*定时器比较匹配/定时器比较匹配和触发低/Pin上升或下降沿/触发高且Pin上升或下降缘/触发下降沿)
timerConfig.timerEnable = kFLEXIO_TimerEnableOnTriggerHigh; //定时器使能条件(始终启动/定时器N-1启动/*触发高/触发高且Pin高/Pin上升沿/Pin上升沿并触发高/触发上升沿/触发上升或下降缘)
timerConfig.timerStop = kFLEXIO_TimerStopBitEnableOnTimerDisable; //定时器停止位生成(禁用/定时器比较匹配时/*定时器失能时/定时器比较匹配或失能时)
timerConfig.timerStart = kFLEXIO_TimerStartBitEnabled; //定时器开始位生成(禁用/*启用)
uint16_t timerDiv = 0;
timerDiv = srcClock_Hz / baudRate_Bps; //srcClock_Hz:FlexIO频率 baudRate_Bps:定时器波特率
timerDiv = timerDiv / 2 - 1; //时钟翻转两次为一个周期
uint16_t timerCmp = 0;
timerCmp = ((uint32_t)(dataMode * 2 - 1U)) << 8U; //dataMode:数据长度
timerCmp |= timerDiv; //高8位和低8位合并
timerConfig.timerCompare = timerCmp ; //两个定时器嵌套工作
//类似:
//for(timerCmp=0;timerCmp<(dataMode*2-1);dataMode++){
// while(timerDiv++<(srcClock_Hz/baudRate_Bps/2-1));
// timerDiv=0;
// SCK_PIN=~SCK_PIN;
//}
FLEXIO_SetTimerConfig(FLEXIO1, 0U, &timerConfig);//index(0-3)定时器源
以上初始化了一根时钟线,在移位寄存器装载数据时启动,比特率为baudRate_Bps
,数据长度为dataMode
,
timerConfig.triggerSelect = FLEXIO_TIMER_TRIGGER_SEL_TIMn(0U);//触发源选择,即满足触发条件定时器开始工作(Pin/移位寄存器/*定时器)
timerConfig.triggerPolarity = kFLEXIO_TimerTriggerPolarityActiveHigh; //触发极性(*高/低)
timerConfig.triggerSource = kFLEXIO_TimerTriggerSourceInternal; //触发源 (*内部/外部)
timerConfig.pinConfig = kFLEXIO_PinConfigOutput; //FlexIO 类型(禁止输出/开漏输出/双向输出/*推挽输出)
timerConfig.pinSelect = START_PIN; //FlexIO 位号(0-31)
timerConfig.pinPolarity = kFLEXIO_PinActiveHigh; //FlexIO 极性(*正极性/负极性)
timerConfig.timerMode = kFLEXIO_TimerModeSingle16Bit; //定时器工作模式(禁用/双8位波特模式/双八位PWM模式/*单16位模式)
timerConfig.timerOutput = kFLEXIO_TimerOutputOneNotAffectedByReset; //定时器初始状态(*逻辑1,并且不受计时器重置的影响/逻辑0,并且不受计时器重置的影响/在启动和重置时为逻辑1/在启动和重置时为逻辑0)
timerConfig.timerDecrement = kFLEXIO_TimerDecSrcOnFlexIOClockShiftTimerOutput;//配置定时器递减源(*FlexIO时钟,移位时钟等于计时器输出/触发输入,移位时钟等于计时器输出/Pin输入,移位时钟等于管脚输入/触发输入,移位时钟等于触发器输入)
timerConfig.timerReset = kFLEXIO_TimerResetNever; //定时器复位条件(*禁用/定时器引脚等于定时器输出时/定时器触发器等于定时器输出时/定时器Pin上升沿/触发源上升沿/触发上升或下降缘)
timerConfig.timerDisable = kFLEXIO_TimerDisableOnPreTimerDisable; //定时器失能条件(禁用/*定时器N-1禁用/定时器比较匹配/定时器比较匹配和触发低/Pin上升或下降沿/触发高且Pin上升或下降缘/触发下降沿)
timerConfig.timerEnable = kFLEXIO_TimerEnableOnPrevTimerEnable; //定时器使能条件(始终启动/*定时器N-1启动/触发高/触发高且Pin高/Pin上升沿/Pin上升沿并触发高/触发上升沿/触发上升或下降缘)
timerConfig.timerStop = kFLEXIO_TimerStopBitDisabled; //定时器停止位生成(*禁用/定时器比较匹配时/定时器失能时/定时器比较匹配或失能时)
timerConfig.timerStart = kFLEXIO_TimerStartBitDisabled; //定时器开始位生成(*禁用/启用)
timerConfig.timerCompare = dataMode * 2 - 1U; //只输出1个周期的波
FLEXIO_SetTimerConfig(FLEXIO1, 0U, &timerConfig);//index(0-3)定时器源
以上初始化了一根时钟线,在定时器0启动时启动,数据为1个周期,用于给从机判断数据开始
开始发送数据只要在FLEXIO1->SHIFTBUFBBS[0]=Data;
把数据装载到寄存器中。
判断完成while(!(FLEXIO_GetShifterStatusFlags(FLEXIO1)&0x01u));//0x01u为对应定时器0=0x01 1=0x02 2=0x04 3=0x08
函数原型:
static inline uint32_t FLEXIO_GetShifterStatusFlags(FLEXIO_Type *base)
{
return ((base->SHIFTSTAT) & FLEXIO_SHIFTSTAT_SSF_MASK);
}
有7个移位器缓冲区寄存器
__IO uint32_t SHIFTBUF[8];
__IO uint32_t SHIFTBUFBIS[8];
__IO uint32_t SHIFTBUFBYS[8];
__IO uint32_t SHIFTBUFBBS[8];
__IO uint32_t SHIFTBUFNBS[8];
__IO uint32_t SHIFTBUFHWS[8];
__IO uint32_t SHIFTBUFNIS[8];
其中关如下:
INPUT 0x87654321 1000 0111 0110 0101 0100 0011 0010 0001
SHIFTBUF 0x84C2A6E1 1000 0100 1100 0010 1010 0110 1110 0001
SHIFTBUFBIS 0x87654321 1000 0111 0110 0101 0100 0011 0010 0001
SHIFTBUFBYS 0xE1A6C284 1110 0001 1010 0110 1100 0010 1000 0100
SHIFTBUFBBS 0x21436587 0010 0001 0100 0011 0110 0101 1000 0111
SHIFTBUFNBS 0x482C6A1E 0100 1000 0010 1100 0110 1010 0001 1110
SHIFTBUFHWS 0xA6E184C2 1010 0110 1110 0001 1000 0100 1100 0010
SHIFTBUFNIS 0x1E6A2C48 0001 1110 0110 1010 0010 1100 0100 1000
以上为一个四线协议
分别是
START_PIN:用于发送数据开始位
SCK_PIN:用于同步时钟
TX_PIN:发送数据,在时钟下降沿发送
RX_PIN:接收数据,在时钟上升沿读取
在dataMode =8
时,输出波形为
START ────┐ ┌─────────────────────────────────────────┐ ┌──
└─────┘ └─────┘
SCK ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──
──┘07└──┘00└──┘01└──┘02└──┘03└──┘04└──┘05└──┘06└──┘07└──┘00└──┘01
TX ┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬──
W─────W─────W─────W─────W─────W─────W─────W─────W─────W─────W──
RX ───┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────
───R─────R─────R─────R─────R─────R─────R─────R─────R─────R─────
以上。