重要的变量、数据结构及功能函数 1. 变量
变量名
变量类型
存储区域
作用
eLedState
enmLSType
全局变量
记录 LED 及数码管的当前状态
ledsta
uint32
静态局部变量
奇数: 开对应的 CAN 收发灯 偶数: 关所有的 CAN 收发灯
CanLedStatus
uint8
静态局部变量
开灯时具体开哪些 CAN 收发灯
uDispPos
uint8
静态局部变量
0: 显示个位 1: 显示十位
LedTimer
uint32
静态局部变量
LED 状态设置时复位,似乎后面没有定时应用
eBoardState
_BOARD_STATE
全局变量
记录模块的当前工作状态
Flash_Data
uint8[1024]
全局变量
无 Bootloader: 从最后扇区(0x0f)读取配置信息 有 Bootloader: 从 Bootloader 的最后一个扇区(0x05)读取配置信息
__Cfg
Cfg_t
全局变量
模块配置信息
__DefCfg
Cfg_t
全局常量
模块默认配置信息
__DaqCanIf
canif_t *
全局 CAN 接口指针
指向可用的 CAN 接口
DeviceStatus
uint8
静态局部变量
记录当前设备状态
DeviceStatusBak
uint8
静态局部变量
记录设备上次的状态,暂没理解到该变量作用
DevStaData
uint8[5]
静态局部变量
状态包向应数据暂存
SndCnt
uint32
全局变量
CAN 发送计数
2. 数据类型 hal.h 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 typedef enum _LED_STATE { LS_XZ8802_ERR, LS_INIT_FAIL, LS_ID_CONFLICTS, LS_ID_OVERRUN, LS_BOARD_TYPEERR, LS_CFG_XZ8802, LS_NORMAL, LS_BOOT }enmLSType; #define DEV_STA_LQD 0 #define DEV_STA_RQD 1 #define DEV_STA_YJGZ 2 #define DEV_STA_RJGZ 3 #define DEV_STA_ZJZC 4 #define DEV_STA_ZJCW 5 #define DEV_STA_DZCX 6 #define DEV_STA_DZCT 7 #define DEV_STA_LXCW 8 #define DEV_STA_RJFW 9
main.c 1 2 3 4 5 6 7 enum _BOARD_STATE { BS_SEND_IDCMD, BS_WAIT_IDCMD, BS_WAIT_TIMEOUT, BS_INIT_OK, BS_RCV_IDCMD }eBoardState;
awx_daocha.h 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 typedef struct { uint32 Ver; uint32 Date; uint32 Range; uint32 ACLinearRange; uint32 DCLinearRange; uint32 ModulusArray[MAX_AI_CHANNEL]; int32 ZeroPointArray[MAX_AI_CHANNEL]; int32 LinearArray[MAX_AI_CHANNEL]; uint32 OverrunRange; uint32 OverrunCnt; uint32 ACChangeRange; uint32 DCChangeRange; uint32 ChangeCnt; uint32 EnableFillDCDY; uint32 Addr; }Cfg_t;
CanIf_Driver.h 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 typedef struct canif { #if MULTI_CAN_IF_EN struct canif *next ; #endif uint32 CanErrCnt:8 ; uint32 CanSndColseCnt:8 ; uint32 CanRcvEventEnable:3 ; uint32 CanLomModeEnable:1 ; uint32 InitBaud:4 ; uint32 WorkBaud:4 ; uint32 SetBaud :4 ; baud_t CanBaud; uint32 (*ioctl) (struct canif *canif,canif_io_type_t type,void *setval); int (*output) (struct canif *canif,CanMsg_t *msg); int (*input) (struct canif *canif,msg_buf_t *pBuf); enum { idle, wait_snd_finish, wait_dly_resnd }snd_sta; void *pSndBuf; uint32 reSndTimerCnt; uint32 ContinuousSndFrames; uint32 SndIntervalMs; struct { void *head; void *end; uint32 cnt; }txbuf_list; #if DELY_SEND_CAN_MSG struct { void *head; void *end; uint32 cnt; }dely_txbuf_list; #endif struct { void *head; void *end; uint32 cnt; }rxbuf_list; void *flag; uint32 BroadAddr; }canif_t ; typedef struct { unsigned char SJW; unsigned char BS1; unsigned char BS2; unsigned int fq; }baud_t ; typedef enum { SetBauad, SetTx, SetRx, Stm32_SetFilter }canif_io_type_t ;
3. 功能函数 3.1. 数据码管显示函数
函数原型: void DispAddr(uint32 addr)
控制数据管显示模块地址或错误代码
每调用一次该函数,就会在个位和十位显示间切换一次
mermaid流程图源码
1 2 3 4 5 6 7 8 9 flowchart LR if1{参数add小于\nDISP_ERROR_CODE_BASE} --> |Y| if2{静态变量\nuDispPos==0} if2 --> |Y| id1(uDispPos=1) --> id2(显示模块地址个位) --> e(返回) if2 --> |N| id3(uDispPos=0) --> id4(显示模块地址十位) --> e if1 --> |N| if3{静态变量\nuDispPos==0} if3 --> |Y| id5(uDispPos=1) --> id6(显示错误代码标识E) --> e(返回) if3 --> |N| id7(uDispPos=0) --> id8(显示错误代码值) --> e
将数码管的查值表放在函数内外及定义成常量和非常量的区别: 到底是将查值表放在函数内还是函数外好,待分析对比。
3.2. led 状态设置函数
函数原型: void SetLedStatus(enmLSType LSType)
根据参数状态,设置每个 led 灯的开、关及闪状态。如果是错误数据管显示错误代码
LED 错误指示
错误代码
Error
Work
CAN1_Tx
CAN1_Rx
加密芯片验证错误
E0
亮
灭
灭
灭
自检错误
E1
亮
灭
灭
灭
地址冲突
E2
亮
灭
闪
灭
地址超限
E3
亮
灭
灭
闪
类型错误
E4
亮
灭
闪
闪
3.3. 简单的软件定时器 说是软件定时器,其实就是一个整形变量。 该变量为值为0
表示未启用该定时器,非0
表示启用该定时器。 定时器复位,就是将 OS 的 Tick 值赋值给该变量。 定时器停止,就是将该变量值归零。 判断定器是否超时,就是比较 OS 当前的 Tick 值与定时器复位时的 Tick 值之差是否大于大于等于需要定时的值。
这种软件定时器,简单方便可以直接放在任务中,不必单独创建一个软件定时任务。 但也容易出现高优先级任务导致低优先级任务中的软定时器超时而得不到响应。所在多任务间的协作要注意
3.4. 读取模块配置信息
函数原型: int ReadConfigFromEeprom(uint32 addr,uint8 *dst,int len,uint8 *def)
函数功能: 从配置区的开始地址处读取指定长度的配置信息,如果读取失败就恢复默认配置
有 Bootloader 时,配置信息放在 Bootloader 的最后一个扇区(0x05)Flash 地址为 0x5000 无 Bootloader 时,配置信息放在 Flash 的最后一个扇区(0x0f)Flash 地址为 0xf000 在整合配置扇区(4k)空间内,应用程序配置信息从 0x100 处开始,前 256 字节(0x00-0xFF)预留给 Bootloader 用
mermaid绘图源码
1 2 3 4 5 6 7 8 9 10 11 12 flowchart LR id1(从Flash配置区中\n应用配置区末尾\n读出CRC值) id2(从Flash配置区中\n应用配置区\n读取全部配置) if1{配置信息\nCRC校验} if2{是否给了\n默认配置} id1 --> id2 --> if1 if1 --> |通过| e1(返回\n配置数据长度) if1 --> |不过| if2 if2 --> |有默认配置| id3(将默认值\n写入应用配置区) --> e2(返回\n0) if2 --> |无默认配置| id4(将配置数据全归0) --> e3(返回\n-1)
3.5. 发送 CAN 数据帧
函数原型: int canif_start_send(canif_t *canif)
函数功能: 从 CAN 接口的发送队列中取取 1 帧数据并发送
flowchart TB
if1{是否\n指定CAN\n接口}
if2{发送功能\n是否关闭}
if3{接口\n是否空闲}
if4{发送指针\n是否为空}
TaskStart 任务
mermaid流程图源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 flowchart TB %% subgraph TaskStart %% direction TB id1(喂看门狗) --> id2(设置 led 状态为正常状态) --> id3(加密芯片检测) id3 -->id4(创建 TaskAwxCp 任务) id4 --> id5(任务延时 5ms) id5 --> if1{判断\n100ms 定时器\n 是否超时} if1 --> |N| if2{led 是否\n 为正常状态} if2 --> |Y| id6(数码管显示模块地址) -->id5 if2 --> |N|id5 subgraph 利用系统 Tick 延时 100ms %% direction LR if1 --> |Y| id7(复位 100ms 定时器) --> id8(再次喂狗) id8 --> if3{led 是否\n 为正常状态} if3 --> |Y| id9(根据静态变量\nledsta 和 CanLedStatus\n 开关 CAN 收发灯) id9 --> id10(关 led 告警灯) if3 --> |N| id11(更新 led 状态) end id10 --> if2 id11 --> if2 %% end
任务优先级为:2
任务堆栈深度为:512
该任务主要完成正常状态下的地址显示和 CAN 收发灯开关
TaskAwxCp 任务 任务流程图
CAN 状态框图
CAN 数据解析流程图 需要解析的命令列表
命令功能
帧类型
命令内容
备注
进入 Boot 模式
标准帧
0xFF , 0xAA , 址址 , 类型 , 随机数 , CRC 检验
取设备历史数据
扩展帧
地址 , 0x03 , 寄存器地址高 , 寄存器地址低 , 数据长度高 , 数据长度低
熄灭设备工作灯
扩展帧
0x01 , 0x10
点亮设备工作灯
扩展帧
0x01 , 0x11
设备类型与分不匹配
扩展帧
0x01 , 0x0A
设备地址超分机配置
扩展帧
0x01 , 0x0B
设备地址冲突
扩展帧
0x01 , 0xFF
取最新的数据
扩展帧
0x01 , 0x61
交流表示电压
取最新的数据
扩展帧
0x01 , 0x64
直流表示电压
读设备配置信息
扩展帧
0x01 , 0x04 | 0x01 , 地址 , 寄存器地址 ,数据长度
写设备配置信息
扩展帧
0x01 , 0x05 | 0x02 , 地址 , 寄存器地址 ,数据 1 , 数据 2 , 数据 3 , 数据 4
数据为小端
mermaid绘图源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 flowchart LR subgraph 设备初始化 direction TB id1(初始化ADC\n并创建TaskAI任务) id2(读取模块\n配置信息) id3(获取模块地址) id4(初始化CAN接口) id5(设置全局CAN接口指针\n__DaqCanIf) id1 --> id2 --> id3 --> id4 --> id5 end id6(根据500ms定时器\n闪烁工作指示灯) --> id7(根据CAN状态\n收发CAN数据)-->id6 设备初始化 --> |通过| id6 设备初始化 --> |不过| 设置初始化失败状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 stateDiagram-v2 state "发送地址检测命令状态" as BS_SEND_IDCMD state "待待检测命令响应状态" as BS_WAIT_IDCMD state "超时未接收到数据状态" as BS_WAIT_TIMEOUT state "初始化成功状态" as BS_INIT_OK state "地址冲突状态" as BS_RCV_IDCMD state if1 <<choice>> [*] --> BS_SEND_IDCMD: 启动发送地址检测 BS_SEND_IDCMD --> BS_WAIT_IDCMD: 发送地址检测命令包 BS_WAIT_IDCMD --> if1 if1 --> BS_RCV_IDCMD: 收到地址冲突帧 BS_RCV_IDCMD --> BS_SEND_IDCMD: 定时器超2s if1 --> BS_WAIT_TIMEOUT: 未收到地址冲突数据包\n且定时器超时2s BS_WAIT_TIMEOUT --> BS_INIT_OK BS_INIT_OK --> BS_INIT_OK: 解析正常数据包 BS_INIT_OK --> BS_SEND_IDCMD: 解析到地址冲突数据包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 flowchart LR id1(收到数据包) if1{帧类型} id2(进入Boot模式) if2{"Data[0]"} e(返回) subgraph Data1["Data[1]"] direction LR sif1{0x10} --> |相等| sid1(灭工作灯) sif2{0x11} --> |相等| sid2(亮工作灯) sif3{0x0A} --> |相等| sid3(显示错误码E4) sif4{0x0B} --> |相等| sid4(显示错误码E3) sif5{0xFF} --> |相等| sid5(显示错误码E2) sif6{0x61} --> |相等| sid6(返最新交流道表数据) sif7{"0x04\n0x01"} --> |相等| sid7(读配置) sif8{"0x05\n0x02"} --> |相等| sid8(写配置) end id1 --> if1 --> |标准帧| id2 if1 --> |扩展帧| if2 if2 --> |== 0x01| Data1 if2 --> |!= 0x01| e Data1 --> e
任务序列图
mermaid绘图源码
1 2 3 4 5 6 7 8 9 10 11 12 sequenceDiagram Note over TaskStart: 优先级: 2 Note over TaskAwxCp: 优先级: 1 Note over TaskAI: 优先级: 3 TaskStart ->> TaskAwxCp: 创建任务TaskAwxCp<br/>并切换至该任务运行 TaskAwxCp -->> TaskAI: 创建任务TaskAI TaskAwxCp ->> TaskAwxCp: 工作灯闪<br/>处理CAN接口数据包 TaskAwxCp ->> TaskStart: TaskAwxCp任务延时 TaskStart ->> TaskStart: 控制CAN收发灯闪<br/>显示模块地址 TaskStart ->> TaskAI: TaskStart任务延时 TaskAI ->> TaskAI: 模拟量采集并存储 TaskAI ->> TaskAwxCp: TaskAI任务延时
值得学习和借鉴之处
线程内通过简单软件定时器实,LED 灯定时闪烁。我之前创建了一个单独的软件定时器线程,有需要定时的就创建一个软件定时器。软件定时器线程的优先级还要比其它应用线程的优先级高,这样保证了软件定时器回调函数能尽可能的及时调用。但线程的开销会更大。
模块配置信息的读写添加了 CRC 检验。