feat: 增加通信帧提取功能
This commit is contained in:
@@ -7,7 +7,9 @@
|
|||||||
"libraries/sdk",
|
"libraries/sdk",
|
||||||
"libraries/zf_common",
|
"libraries/zf_common",
|
||||||
"libraries/zf_device",
|
"libraries/zf_device",
|
||||||
"libraries/zf_driver"
|
"libraries/zf_driver",
|
||||||
|
"3rd-lib/crc16",
|
||||||
|
"3rd-lib/lwrb"
|
||||||
],
|
],
|
||||||
"virtualFolder": {
|
"virtualFolder": {
|
||||||
"name": "<virtual_root>",
|
"name": "<virtual_root>",
|
||||||
@@ -55,7 +57,11 @@
|
|||||||
"libraries/sdk/Core",
|
"libraries/sdk/Core",
|
||||||
"libraries/zf_common",
|
"libraries/zf_common",
|
||||||
"libraries/zf_device",
|
"libraries/zf_device",
|
||||||
"libraries/zf_driver"
|
"libraries/zf_driver",
|
||||||
|
"3rd-lib/crc16",
|
||||||
|
"app/page",
|
||||||
|
"3rd-lib/lwrb/inc",
|
||||||
|
"app/tiny_frame"
|
||||||
],
|
],
|
||||||
"libList": [
|
"libList": [
|
||||||
"libraries/zf_device"
|
"libraries/zf_device"
|
||||||
|
|||||||
137
3rd-lib/crc16/crc16.c
Normal file
137
3rd-lib/crc16/crc16.c
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
|
||||||
|
#include "crc16.h"
|
||||||
|
|
||||||
|
//ModBUS CRC-16 码(modbus)校验
|
||||||
|
|
||||||
|
/*
|
||||||
|
1、实时计算 CRC16
|
||||||
|
该种方式耗时比较多,但占用 FLASH、RAM 小
|
||||||
|
|
||||||
|
1)CRC 寄存器初始值为 FFFF;即 16 个字节全为 1;
|
||||||
|
2)CRC-16 / MODBUS 的多项式 A001H (1010 0000 0000 0001B) ‘H’表示 16 进制数,‘B’表示二进制数
|
||||||
|
|
||||||
|
计算步骤为:
|
||||||
|
(1).预置 16 位寄存器为十六进制 FFFF(即全为 1) ,称此寄存器为 CRC 寄存器;
|
||||||
|
(2).把第一个 8 位数据与 16 位 CRC 寄存器的低位相异或,把结果放于 CRC 寄存器;
|
||||||
|
(3).检测相异或后的 CRC 寄存器的最低位,若最低位为 1:CRC 寄存器先右移 1 位,再与多项式 A001H 进行异或;若为 0,则 CRC 寄存器右移 1 位,无需与多项式进行异或。
|
||||||
|
(4).重复步骤 3,直到右移 8 次,这样整个 8 位数据全部进行了处理;
|
||||||
|
(5).重复步骤 2 到步骤 4,进行下一个 8 位数据的处理;
|
||||||
|
(6).最后得到的 CRC 寄存器即为 CRC 码。
|
||||||
|
|
||||||
|
addr:需要校验的字节数组
|
||||||
|
num:需要校验的字节数
|
||||||
|
返回值:16 位的 CRC 校验码
|
||||||
|
*/
|
||||||
|
uint16_t crc16(uint8_t *addr,uint32_t num)
|
||||||
|
{
|
||||||
|
int i,j,temp;
|
||||||
|
uint16_t crc=0xFFFF;
|
||||||
|
for(i=0;i<num;i++)
|
||||||
|
{
|
||||||
|
crc=crc^(*addr);
|
||||||
|
for(j=0;j<8;j++)
|
||||||
|
{
|
||||||
|
temp=crc&0x0001;
|
||||||
|
crc=crc>>1;
|
||||||
|
if(temp)
|
||||||
|
{
|
||||||
|
crc=crc^0xA001;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addr++;
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
|
||||||
|
//将 CRC 校验的高低位对换位置
|
||||||
|
// uint16_t crc1;
|
||||||
|
// crc1 = crc >> 8;
|
||||||
|
// crc1 = crc1 | (crc << 8);
|
||||||
|
// return crc1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
2、查表计算 CRC16(耗时少)
|
||||||
|
从上面代码中,可以看出 CRC-16 的漏洞。无非是将待计算的数组,从头到尾单个字节按照同一算法反复计算。每个元素只有 0~255 的输入可能性,算法固定,所以算出来的值也是固定的。
|
||||||
|
于是可以提前计算出校验值,按照输入为 0~255,排列为一组校验输出表。计算过程中根据输入数据进行查表,从头查到尾得到最终校验值。
|
||||||
|
这样的计算方式省去了单片机的反复计算,极大缩短了计算耗时。而且可以将查表的数组类型定义为 const,存放在 Flash 中,运行时不占用 RAM。虽然都是空间换时间的策略,但这种方式只占用 Flash 不占用珍贵的 RAM。
|
||||||
|
|
||||||
|
addr:需要校验的字节数组
|
||||||
|
num:需要校验的字节数
|
||||||
|
返回值:16 位的 CRC 校验码
|
||||||
|
说 明:计算结果是高位在前,需要转换才能发送
|
||||||
|
*/
|
||||||
|
uint16_t crc16_check(uint8_t* addr, uint32_t num)
|
||||||
|
{
|
||||||
|
// CRC 高位字节值表
|
||||||
|
static const uint8_t auchCRCHi[] =
|
||||||
|
{
|
||||||
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
|
||||||
|
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||||
|
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
|
||||||
|
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
||||||
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
|
||||||
|
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
|
||||||
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
|
||||||
|
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||||
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
|
||||||
|
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
|
||||||
|
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
|
||||||
|
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
||||||
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
|
||||||
|
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
|
||||||
|
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
|
||||||
|
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
||||||
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
|
||||||
|
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||||
|
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
|
||||||
|
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||||
|
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
|
||||||
|
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
|
||||||
|
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
|
||||||
|
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||||
|
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
|
||||||
|
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
|
||||||
|
} ;
|
||||||
|
// CRC 低位字节值表
|
||||||
|
static const uint8_t auchCRCLo[] =
|
||||||
|
{
|
||||||
|
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
|
||||||
|
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
|
||||||
|
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
|
||||||
|
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
|
||||||
|
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
|
||||||
|
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
|
||||||
|
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
|
||||||
|
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
|
||||||
|
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
|
||||||
|
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
|
||||||
|
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
|
||||||
|
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
|
||||||
|
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
|
||||||
|
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
|
||||||
|
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
|
||||||
|
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
|
||||||
|
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
|
||||||
|
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
|
||||||
|
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
|
||||||
|
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
|
||||||
|
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
|
||||||
|
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
|
||||||
|
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
|
||||||
|
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
|
||||||
|
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
|
||||||
|
0x43, 0x83, 0x41, 0x81, 0x80, 0x40
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t uchCRCHi = 0xFF;
|
||||||
|
uint8_t uchCRCLo = 0xFF;
|
||||||
|
uint16_t uIndex;
|
||||||
|
while(num--)
|
||||||
|
{
|
||||||
|
uIndex = uchCRCLo ^ *addr++;
|
||||||
|
uchCRCLo = uchCRCHi^auchCRCHi[uIndex];
|
||||||
|
uchCRCHi = auchCRCLo[uIndex];
|
||||||
|
}
|
||||||
|
return (uchCRCHi<<8|uchCRCLo);
|
||||||
|
|
||||||
|
}
|
||||||
9
3rd-lib/crc16/crc16.h
Normal file
9
3rd-lib/crc16/crc16.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#ifndef __CRC_16_H
|
||||||
|
#define __CRC_16_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern uint16_t crc16(uint8_t *addr,uint32_t num); // 实时计算方式
|
||||||
|
extern uint16_t crc16_check(uint8_t* addr, uint32_t num); // 查表计算方式
|
||||||
|
|
||||||
|
#endif
|
||||||
149
3rd-lib/lwrb/inc/lwrb.h
Normal file
149
3rd-lib/lwrb/inc/lwrb.h
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
/**
|
||||||
|
* \file lwrb.h
|
||||||
|
* \brief LwRB - Lightweight ring buffer
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Tilen MAJERLE
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person
|
||||||
|
* obtaining a copy of this software and associated documentation
|
||||||
|
* files (the "Software"), to deal in the Software without restriction,
|
||||||
|
* including without limitation the rights to use, copy, modify, merge,
|
||||||
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of LwRB - Lightweight ring buffer library.
|
||||||
|
*
|
||||||
|
* Author: Tilen MAJERLE <tilen@majerle.eu>
|
||||||
|
* Version: v3.0.0-rc1
|
||||||
|
*/
|
||||||
|
#ifndef LWRB_HDR_H
|
||||||
|
#define LWRB_HDR_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \defgroup LWRB Lightweight ring buffer manager
|
||||||
|
* \brief Lightweight ring buffer manager
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(LWRB_DISABLE_ATOMIC) || __DOXYGEN__
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Atomic type for size variable.
|
||||||
|
* Default value is set to be `unsigned 32-bits` type
|
||||||
|
*/
|
||||||
|
typedef atomic_ulong lwrb_sz_atomic_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Size variable for all library operations.
|
||||||
|
* Default value is set to be `unsigned 32-bits` type
|
||||||
|
*/
|
||||||
|
typedef unsigned long lwrb_sz_t;
|
||||||
|
#else
|
||||||
|
typedef unsigned long lwrb_sz_atomic_t;
|
||||||
|
typedef unsigned long lwrb_sz_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Event type for buffer operations
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LWRB_EVT_READ, /*!< Read event */
|
||||||
|
LWRB_EVT_WRITE, /*!< Write event */
|
||||||
|
LWRB_EVT_RESET, /*!< Reset event */
|
||||||
|
} lwrb_evt_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Buffer structure forward declaration
|
||||||
|
*/
|
||||||
|
struct lwrb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Event callback function type
|
||||||
|
* \param[in] buff: Buffer handle for event
|
||||||
|
* \param[in] evt: Event type
|
||||||
|
* \param[in] bp: Number of bytes written or read (when used), depends on event type
|
||||||
|
*/
|
||||||
|
typedef void (*lwrb_evt_fn)(struct lwrb* buff, lwrb_evt_type_t evt, lwrb_sz_t bp);
|
||||||
|
|
||||||
|
/* List of flags */
|
||||||
|
#define LWRB_FLAG_READ_ALL ((uint16_t)0x0001)
|
||||||
|
#define LWRB_FLAG_WRITE_ALL ((uint16_t)0x0001)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Buffer structure
|
||||||
|
*/
|
||||||
|
typedef struct lwrb {
|
||||||
|
uint8_t* buff; /*!< Pointer to buffer data. Buffer is considered initialized when `buff != NULL` and `size > 0` */
|
||||||
|
lwrb_sz_t size; /*!< Size of buffer data. Size of actual buffer is `1` byte less than value holds */
|
||||||
|
lwrb_sz_atomic_t r; /*!< Next read pointer. Buffer is considered empty when `r == w` and full when `w == r - 1` */
|
||||||
|
lwrb_sz_atomic_t w; /*!< Next write pointer. Buffer is considered empty when `r == w` and full when `w == r - 1` */
|
||||||
|
lwrb_evt_fn evt_fn; /*!< Pointer to event callback function */
|
||||||
|
} lwrb_t;
|
||||||
|
|
||||||
|
uint8_t lwrb_init(lwrb_t* buff, void* buffdata, lwrb_sz_t size);
|
||||||
|
uint8_t lwrb_is_ready(lwrb_t* buff);
|
||||||
|
void lwrb_free(lwrb_t* buff);
|
||||||
|
void lwrb_reset(lwrb_t* buff);
|
||||||
|
void lwrb_set_evt_fn(lwrb_t* buff, lwrb_evt_fn fn);
|
||||||
|
|
||||||
|
/* Read/Write functions */
|
||||||
|
lwrb_sz_t lwrb_write(lwrb_t* buff, const void* data, lwrb_sz_t btw);
|
||||||
|
lwrb_sz_t lwrb_read(lwrb_t* buff, void* data, lwrb_sz_t btr);
|
||||||
|
lwrb_sz_t lwrb_peek(const lwrb_t* buff, lwrb_sz_t skip_count, void* data, lwrb_sz_t btp);
|
||||||
|
|
||||||
|
/* Extended read/write functions */
|
||||||
|
uint8_t lwrb_write_ex(lwrb_t* buff, const void* data, lwrb_sz_t btw, lwrb_sz_t* bw, uint16_t flags);
|
||||||
|
uint8_t lwrb_read_ex(lwrb_t* buff, void* data, lwrb_sz_t btr, lwrb_sz_t* br, uint16_t flags);
|
||||||
|
|
||||||
|
/* Buffer size information */
|
||||||
|
lwrb_sz_t lwrb_get_free(const lwrb_t* buff);
|
||||||
|
lwrb_sz_t lwrb_get_full(const lwrb_t* buff);
|
||||||
|
|
||||||
|
/* Read data block management */
|
||||||
|
void* lwrb_get_linear_block_read_address(const lwrb_t* buff);
|
||||||
|
lwrb_sz_t lwrb_get_linear_block_read_length(const lwrb_t* buff);
|
||||||
|
lwrb_sz_t lwrb_skip(lwrb_t* buff, lwrb_sz_t len);
|
||||||
|
|
||||||
|
/* Write data block management */
|
||||||
|
void* lwrb_get_linear_block_write_address(const lwrb_t* buff);
|
||||||
|
lwrb_sz_t lwrb_get_linear_block_write_length(const lwrb_t* buff);
|
||||||
|
lwrb_sz_t lwrb_advance(lwrb_t* buff, lwrb_sz_t len);
|
||||||
|
|
||||||
|
/* Search in buffer */
|
||||||
|
uint8_t lwrb_find(const lwrb_t* buff, const void* bts, lwrb_sz_t len, lwrb_sz_t start_offset, lwrb_sz_t* found_idx);
|
||||||
|
lwrb_sz_t lwrb_overwrite(lwrb_t* buff, const void* data, lwrb_sz_t btw);
|
||||||
|
lwrb_sz_t lwrb_move(lwrb_t* dest, lwrb_t* src);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* LWRB_HDR_H */
|
||||||
645
3rd-lib/lwrb/src/lwrb.c
Normal file
645
3rd-lib/lwrb/src/lwrb.c
Normal file
@@ -0,0 +1,645 @@
|
|||||||
|
/**
|
||||||
|
* \file lwrb.c
|
||||||
|
* \brief Lightweight ring buffer
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Tilen MAJERLE
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person
|
||||||
|
* obtaining a copy of this software and associated documentation
|
||||||
|
* files (the "Software"), to deal in the Software without restriction,
|
||||||
|
* including without limitation the rights to use, copy, modify, merge,
|
||||||
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||||
|
* and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is part of LwRB - Lightweight ring buffer library.
|
||||||
|
*
|
||||||
|
* Author: Tilen MAJERLE <tilen@majerle.eu>
|
||||||
|
* Version: v3.0.0
|
||||||
|
*/
|
||||||
|
#include "lwrb.h"
|
||||||
|
|
||||||
|
/* Memory set and copy functions */
|
||||||
|
#define BUF_MEMSET memset
|
||||||
|
#define BUF_MEMCPY memcpy
|
||||||
|
|
||||||
|
#define BUF_IS_VALID(b) ((b) != NULL && (b)->buff != NULL && (b)->size > 0)
|
||||||
|
#define BUF_MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||||
|
#define BUF_MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||||
|
#define BUF_SEND_EVT(b, type, bp) \
|
||||||
|
do { \
|
||||||
|
if ((b)->evt_fn != NULL) { \
|
||||||
|
(b)->evt_fn((void*)(b), (type), (bp)); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Optional atomic opeartions */
|
||||||
|
#ifdef LWRB_DISABLE_ATOMIC
|
||||||
|
#define LWRB_INIT(var, val) (var) = (val)
|
||||||
|
#define LWRB_LOAD(var, type) (var)
|
||||||
|
#define LWRB_STORE(var, val, type) (var) = (val)
|
||||||
|
#else
|
||||||
|
#define LWRB_INIT(var, val) atomic_init(&(var), (val))
|
||||||
|
#define LWRB_LOAD(var, type) atomic_load_explicit(&(var), (type))
|
||||||
|
#define LWRB_STORE(var, val, type) atomic_store_explicit(&(var), (val), (type))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Initialize buffer handle to default values with size and buffer data array
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
* \param[in] buffdata: Pointer to memory to use as buffer data
|
||||||
|
* \param[in] size: Size of `buffdata` in units of bytes
|
||||||
|
* Maximum number of bytes buffer can hold is `size - 1`
|
||||||
|
* \return `1` on success, `0` otherwise
|
||||||
|
*/
|
||||||
|
uint8_t
|
||||||
|
lwrb_init(lwrb_t* buff, void* buffdata, lwrb_sz_t size) {
|
||||||
|
if (buff == NULL || buffdata == NULL || size == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
buff->evt_fn = NULL;
|
||||||
|
buff->size = size;
|
||||||
|
buff->buff = buffdata;
|
||||||
|
LWRB_INIT(buff->w, 0);
|
||||||
|
LWRB_INIT(buff->r, 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Check if buff is initialized and ready to use
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
* \return `1` if ready, `0` otherwise
|
||||||
|
*/
|
||||||
|
uint8_t
|
||||||
|
lwrb_is_ready(lwrb_t* buff) {
|
||||||
|
return BUF_IS_VALID(buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Free buffer memory
|
||||||
|
* \note Since implementation does not use dynamic allocation,
|
||||||
|
* it just sets buffer handle to `NULL`
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
lwrb_free(lwrb_t* buff) {
|
||||||
|
if (BUF_IS_VALID(buff)) {
|
||||||
|
buff->buff = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Set event function callback for different buffer operations
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
* \param[in] evt_fn: Callback function
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
lwrb_set_evt_fn(lwrb_t* buff, lwrb_evt_fn evt_fn) {
|
||||||
|
if (BUF_IS_VALID(buff)) {
|
||||||
|
buff->evt_fn = evt_fn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Write data to buffer.
|
||||||
|
* Copies data from `data` array to buffer and marks buffer as full for maximum `btw` number of bytes
|
||||||
|
*
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
* \param[in] data: Pointer to data to write into buffer
|
||||||
|
* \param[in] btw: Number of bytes to write
|
||||||
|
* \return Number of bytes written to buffer.
|
||||||
|
* When returned value is less than `btw`, there was no enough memory available
|
||||||
|
* to copy full data array.
|
||||||
|
*/
|
||||||
|
lwrb_sz_t
|
||||||
|
lwrb_write(lwrb_t* buff, const void* data, lwrb_sz_t btw) {
|
||||||
|
lwrb_sz_t written = 0;
|
||||||
|
|
||||||
|
if (lwrb_write_ex(buff, data, btw, &written, 0)) {
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Write extended functionality
|
||||||
|
*
|
||||||
|
* \param buff: Ring buffer instance
|
||||||
|
* \param data: Pointer to data to write into buffer
|
||||||
|
* \param btw: Number of bytes to write
|
||||||
|
* \param bw: Output pointer to write number of bytes written
|
||||||
|
* \param flags: Optional flags.
|
||||||
|
* \ref LWRB_FLAG_WRITE_ALL: Request to write all data (up to btw).
|
||||||
|
* Will early return if no memory available
|
||||||
|
* \return `1` if write operation OK, `0` otherwise
|
||||||
|
*/
|
||||||
|
uint8_t
|
||||||
|
lwrb_write_ex(lwrb_t* buff, const void* data, lwrb_sz_t btw, lwrb_sz_t* bw, uint16_t flags) {
|
||||||
|
lwrb_sz_t tocopy, free, buff_w_ptr;
|
||||||
|
const uint8_t* d = data;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff) || data == NULL || btw == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate maximum number of bytes available to write */
|
||||||
|
free = lwrb_get_free(buff);
|
||||||
|
/* If no memory, or if user wants to write ALL data but no enough space, exit early */
|
||||||
|
if (free == 0 || (free < btw && flags & LWRB_FLAG_WRITE_ALL)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
btw = BUF_MIN(free, btw);
|
||||||
|
buff_w_ptr = LWRB_LOAD(buff->w, memory_order_acquire);
|
||||||
|
|
||||||
|
/* Step 1: Write data to linear part of buffer */
|
||||||
|
tocopy = BUF_MIN(buff->size - buff_w_ptr, btw);
|
||||||
|
BUF_MEMCPY(&buff->buff[buff_w_ptr], d, tocopy);
|
||||||
|
buff_w_ptr += tocopy;
|
||||||
|
btw -= tocopy;
|
||||||
|
|
||||||
|
/* Step 2: Write data to beginning of buffer (overflow part) */
|
||||||
|
if (btw > 0) {
|
||||||
|
BUF_MEMCPY(buff->buff, &d[tocopy], btw);
|
||||||
|
buff_w_ptr = btw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 3: Check end of buffer */
|
||||||
|
if (buff_w_ptr >= buff->size) {
|
||||||
|
buff_w_ptr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write final value to the actual running variable.
|
||||||
|
* This is to ensure no read operation can access intermediate data
|
||||||
|
*/
|
||||||
|
LWRB_STORE(buff->w, buff_w_ptr, memory_order_release);
|
||||||
|
|
||||||
|
BUF_SEND_EVT(buff, LWRB_EVT_WRITE, tocopy + btw);
|
||||||
|
if (bw != NULL) {
|
||||||
|
*bw = tocopy + btw;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read data from buffer.
|
||||||
|
* Copies data from buffer to `data` array and marks buffer as free for maximum `btr` number of bytes
|
||||||
|
*
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
* \param[out] data: Pointer to output memory to copy buffer data to
|
||||||
|
* \param[in] btr: Number of bytes to read
|
||||||
|
* \return Number of bytes read and copied to data array
|
||||||
|
*/
|
||||||
|
lwrb_sz_t
|
||||||
|
lwrb_read(lwrb_t* buff, void* data, lwrb_sz_t btr) {
|
||||||
|
lwrb_sz_t read = 0;
|
||||||
|
|
||||||
|
if (lwrb_read_ex(buff, data, btr, &read, 0)) {
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Write extended functionality
|
||||||
|
*
|
||||||
|
* \param buff: Ring buffer instance
|
||||||
|
* \param data: Pointer to memory to write read data from buffer
|
||||||
|
* \param btr: Number of bytes to read
|
||||||
|
* \param br: Output pointer to write number of bytes read
|
||||||
|
* \param flags: Optional flags
|
||||||
|
* \ref LWRB_FLAG_READ_ALL: Request to read all data (up to btr).
|
||||||
|
* Will early return if no enough bytes in the buffer
|
||||||
|
* \return `1` if read operation OK, `0` otherwise
|
||||||
|
*/
|
||||||
|
uint8_t
|
||||||
|
lwrb_read_ex(lwrb_t* buff, void* data, lwrb_sz_t btr, lwrb_sz_t* br, uint16_t flags) {
|
||||||
|
lwrb_sz_t tocopy, full, buff_r_ptr;
|
||||||
|
uint8_t* d = data;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff) || data == NULL || btr == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate maximum number of bytes available to read */
|
||||||
|
full = lwrb_get_full(buff);
|
||||||
|
if (full == 0 || (full < btr && (flags & LWRB_FLAG_READ_ALL))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
btr = BUF_MIN(full, btr);
|
||||||
|
buff_r_ptr = LWRB_LOAD(buff->r, memory_order_acquire);
|
||||||
|
|
||||||
|
/* Step 1: Read data from linear part of buffer */
|
||||||
|
tocopy = BUF_MIN(buff->size - buff_r_ptr, btr);
|
||||||
|
BUF_MEMCPY(d, &buff->buff[buff_r_ptr], tocopy);
|
||||||
|
buff_r_ptr += tocopy;
|
||||||
|
btr -= tocopy;
|
||||||
|
|
||||||
|
/* Step 2: Read data from beginning of buffer (overflow part) */
|
||||||
|
if (btr > 0) {
|
||||||
|
BUF_MEMCPY(&d[tocopy], buff->buff, btr);
|
||||||
|
buff_r_ptr = btr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 3: Check end of buffer */
|
||||||
|
if (buff_r_ptr >= buff->size) {
|
||||||
|
buff_r_ptr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write final value to the actual running variable.
|
||||||
|
* This is to ensure no write operation can access intermediate data
|
||||||
|
*/
|
||||||
|
LWRB_STORE(buff->r, buff_r_ptr, memory_order_release);
|
||||||
|
|
||||||
|
BUF_SEND_EVT(buff, LWRB_EVT_READ, tocopy + btr);
|
||||||
|
if (br != NULL) {
|
||||||
|
*br = tocopy + btr;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read from buffer without changing read pointer (peek only)
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
* \param[in] skip_count: Number of bytes to skip before reading data
|
||||||
|
* \param[out] data: Pointer to output memory to copy buffer data to
|
||||||
|
* \param[in] btp: Number of bytes to peek
|
||||||
|
* \return Number of bytes peeked and written to output array
|
||||||
|
*/
|
||||||
|
lwrb_sz_t
|
||||||
|
lwrb_peek(const lwrb_t* buff, lwrb_sz_t skip_count, void* data, lwrb_sz_t btp) {
|
||||||
|
lwrb_sz_t full, tocopy, r;
|
||||||
|
uint8_t* d = data;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff) || data == NULL || btp == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate maximum number of bytes available to read
|
||||||
|
* and check if we can even fit to it
|
||||||
|
*/
|
||||||
|
full = lwrb_get_full(buff);
|
||||||
|
if (skip_count >= full) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
r = LWRB_LOAD(buff->r, memory_order_relaxed);
|
||||||
|
r += skip_count;
|
||||||
|
full -= skip_count;
|
||||||
|
if (r >= buff->size) {
|
||||||
|
r -= buff->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check maximum number of bytes available to read after skip */
|
||||||
|
btp = BUF_MIN(full, btp);
|
||||||
|
if (btp == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 1: Read data from linear part of buffer */
|
||||||
|
tocopy = BUF_MIN(buff->size - r, btp);
|
||||||
|
BUF_MEMCPY(d, &buff->buff[r], tocopy);
|
||||||
|
btp -= tocopy;
|
||||||
|
|
||||||
|
/* Step 2: Read data from beginning of buffer (overflow part) */
|
||||||
|
if (btp > 0) {
|
||||||
|
BUF_MEMCPY(&d[tocopy], buff->buff, btp);
|
||||||
|
}
|
||||||
|
return tocopy + btp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get available size in buffer for write operation
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
* \return Number of free bytes in memory
|
||||||
|
*/
|
||||||
|
lwrb_sz_t
|
||||||
|
lwrb_get_free(const lwrb_t* buff) {
|
||||||
|
lwrb_sz_t size, w, r;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy buffer pointers to local variables with atomic access.
|
||||||
|
*
|
||||||
|
* To ensure thread safety (only when in single-entry, single-exit FIFO mode use case),
|
||||||
|
* it is important to write buffer r and w values to local w and r variables.
|
||||||
|
*
|
||||||
|
* Local variables will ensure below if statements will always use the same value,
|
||||||
|
* even if buff->w or buff->r get changed during interrupt processing.
|
||||||
|
*
|
||||||
|
* They may change during load operation, important is that
|
||||||
|
* they do not change during if-elseif-else operations following these assignments.
|
||||||
|
*
|
||||||
|
* lwrb_get_free is only called for write purpose, and when in FIFO mode, then:
|
||||||
|
* - buff->w pointer will not change by another process/interrupt because we are in write mode just now
|
||||||
|
* - buff->r pointer may change by another process. If it gets changed after buff->r has been loaded to local variable,
|
||||||
|
* buffer will see "free size" less than it actually is. This is not a problem, application can
|
||||||
|
* always try again to write more data to remaining free memory that was read just during copy operation
|
||||||
|
*/
|
||||||
|
w = LWRB_LOAD(buff->w, memory_order_relaxed);
|
||||||
|
r = LWRB_LOAD(buff->r, memory_order_relaxed);
|
||||||
|
|
||||||
|
if (w == r) {
|
||||||
|
size = buff->size;
|
||||||
|
} else if (r > w) {
|
||||||
|
size = r - w;
|
||||||
|
} else {
|
||||||
|
size = buff->size - (w - r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buffer free size is always 1 less than actual size */
|
||||||
|
return size - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get number of bytes currently available in buffer
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
* \return Number of bytes ready to be read
|
||||||
|
*/
|
||||||
|
lwrb_sz_t
|
||||||
|
lwrb_get_full(const lwrb_t* buff) {
|
||||||
|
lwrb_sz_t size, w, r;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy buffer pointers to local variables.
|
||||||
|
*
|
||||||
|
* To ensure thread safety (only when in single-entry, single-exit FIFO mode use case),
|
||||||
|
* it is important to write buffer r and w values to local w and r variables.
|
||||||
|
*
|
||||||
|
* Local variables will ensure below if statements will always use the same value,
|
||||||
|
* even if buff->w or buff->r get changed during interrupt processing.
|
||||||
|
*
|
||||||
|
* They may change during load operation, important is that
|
||||||
|
* they do not change during if-elseif-else operations following these assignments.
|
||||||
|
*
|
||||||
|
* lwrb_get_full is only called for read purpose, and when in FIFO mode, then:
|
||||||
|
* - buff->r pointer will not change by another process/interrupt because we are in read mode just now
|
||||||
|
* - buff->w pointer may change by another process. If it gets changed after buff->w has been loaded to local variable,
|
||||||
|
* buffer will see "full size" less than it really is. This is not a problem, application can
|
||||||
|
* always try again to read more data from remaining full memory that was written just during copy operation
|
||||||
|
*/
|
||||||
|
w = LWRB_LOAD(buff->w, memory_order_relaxed);
|
||||||
|
r = LWRB_LOAD(buff->r, memory_order_relaxed);
|
||||||
|
|
||||||
|
if (w == r) {
|
||||||
|
size = 0;
|
||||||
|
} else if (w > r) {
|
||||||
|
size = w - r;
|
||||||
|
} else {
|
||||||
|
size = buff->size - (r - w);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Resets buffer to default values. Buffer size is not modified
|
||||||
|
* \note This function is not thread safe.
|
||||||
|
* When used, application must ensure there is no active read/write operation
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
lwrb_reset(lwrb_t* buff) {
|
||||||
|
if (BUF_IS_VALID(buff)) {
|
||||||
|
LWRB_STORE(buff->w, 0, memory_order_release);
|
||||||
|
LWRB_STORE(buff->r, 0, memory_order_release);
|
||||||
|
BUF_SEND_EVT(buff, LWRB_EVT_RESET, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get linear address for buffer for fast read
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
* \return Linear buffer start address
|
||||||
|
*/
|
||||||
|
void*
|
||||||
|
lwrb_get_linear_block_read_address(const lwrb_t* buff) {
|
||||||
|
if (!BUF_IS_VALID(buff)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return &buff->buff[buff->r];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get length of linear block address before it overflows for read operation
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
* \return Linear buffer size in units of bytes for read operation
|
||||||
|
*/
|
||||||
|
lwrb_sz_t
|
||||||
|
lwrb_get_linear_block_read_length(const lwrb_t* buff) {
|
||||||
|
lwrb_sz_t len, w, r;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use temporary values in case they are changed during operations.
|
||||||
|
* See lwrb_buff_free or lwrb_buff_full functions for more information why this is OK.
|
||||||
|
*/
|
||||||
|
w = LWRB_LOAD(buff->w, memory_order_relaxed);
|
||||||
|
r = LWRB_LOAD(buff->r, memory_order_relaxed);
|
||||||
|
|
||||||
|
if (w > r) {
|
||||||
|
len = w - r;
|
||||||
|
} else if (r > w) {
|
||||||
|
len = buff->size - r;
|
||||||
|
} else {
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Skip (ignore; advance read pointer) buffer data
|
||||||
|
* Marks data as read in the buffer and increases free memory for up to `len` bytes
|
||||||
|
*
|
||||||
|
* \note Useful at the end of streaming transfer such as DMA
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
* \param[in] len: Number of bytes to skip and mark as read
|
||||||
|
* \return Number of bytes skipped
|
||||||
|
*/
|
||||||
|
lwrb_sz_t
|
||||||
|
lwrb_skip(lwrb_t* buff, lwrb_sz_t len) {
|
||||||
|
lwrb_sz_t full, r;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff) || len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
full = lwrb_get_full(buff);
|
||||||
|
len = BUF_MIN(len, full);
|
||||||
|
r = LWRB_LOAD(buff->r, memory_order_acquire);
|
||||||
|
r += len;
|
||||||
|
if (r >= buff->size) {
|
||||||
|
r -= buff->size;
|
||||||
|
}
|
||||||
|
LWRB_STORE(buff->r, r, memory_order_release);
|
||||||
|
BUF_SEND_EVT(buff, LWRB_EVT_READ, len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get linear address for buffer for fast read
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
* \return Linear buffer start address
|
||||||
|
*/
|
||||||
|
void*
|
||||||
|
lwrb_get_linear_block_write_address(const lwrb_t* buff) {
|
||||||
|
if (!BUF_IS_VALID(buff)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return &buff->buff[buff->w];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get length of linear block address before it overflows for write operation
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
* \return Linear buffer size in units of bytes for write operation
|
||||||
|
*/
|
||||||
|
lwrb_sz_t
|
||||||
|
lwrb_get_linear_block_write_length(const lwrb_t* buff) {
|
||||||
|
lwrb_sz_t len, w, r;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use temporary values in case they are changed during operations.
|
||||||
|
* See lwrb_buff_free or lwrb_buff_full functions for more information why this is OK.
|
||||||
|
*/
|
||||||
|
w = LWRB_LOAD(buff->w, memory_order_relaxed);
|
||||||
|
r = LWRB_LOAD(buff->r, memory_order_relaxed);
|
||||||
|
|
||||||
|
if (w >= r) {
|
||||||
|
len = buff->size - w;
|
||||||
|
/*
|
||||||
|
* When read pointer is 0,
|
||||||
|
* maximal length is one less as if too many bytes
|
||||||
|
* are written, buffer would be considered empty again (r == w)
|
||||||
|
*/
|
||||||
|
if (r == 0) {
|
||||||
|
/*
|
||||||
|
* Cannot overflow:
|
||||||
|
* - If r is not 0, statement does not get called
|
||||||
|
* - buff->size cannot be 0 and if r is 0, len is greater 0
|
||||||
|
*/
|
||||||
|
--len;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
len = r - w - 1;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Advance write pointer in the buffer.
|
||||||
|
* Similar to skip function but modifies write pointer instead of read
|
||||||
|
*
|
||||||
|
* \note Useful when hardware is writing to buffer and application needs to increase number
|
||||||
|
* of bytes written to buffer by hardware
|
||||||
|
* \param[in] buff: Ring buffer instance
|
||||||
|
* \param[in] len: Number of bytes to advance
|
||||||
|
* \return Number of bytes advanced for write operation
|
||||||
|
*/
|
||||||
|
lwrb_sz_t
|
||||||
|
lwrb_advance(lwrb_t* buff, lwrb_sz_t len) {
|
||||||
|
lwrb_sz_t free, w;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff) || len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use local variables before writing back to main structure */
|
||||||
|
free = lwrb_get_free(buff);
|
||||||
|
len = BUF_MIN(len, free);
|
||||||
|
w = LWRB_LOAD(buff->w, memory_order_acquire);
|
||||||
|
w += len;
|
||||||
|
if (w >= buff->size) {
|
||||||
|
w -= buff->size;
|
||||||
|
}
|
||||||
|
LWRB_STORE(buff->w, w, memory_order_release);
|
||||||
|
BUF_SEND_EVT(buff, LWRB_EVT_WRITE, len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Searches for a *needle* in an array, starting from given offset.
|
||||||
|
*
|
||||||
|
* \note This function is not thread-safe.
|
||||||
|
*
|
||||||
|
* \param buff: Ring buffer to search for needle in
|
||||||
|
* \param bts: Constant byte array sequence to search for in a buffer
|
||||||
|
* \param len: Length of the \arg bts array
|
||||||
|
* \param start_offset: Start offset in the buffer
|
||||||
|
* \param found_idx: Pointer to variable to write index in array where bts has been found
|
||||||
|
* Must not be set to `NULL`
|
||||||
|
* \return `1` if \arg bts found, `0` otherwise
|
||||||
|
*/
|
||||||
|
uint8_t
|
||||||
|
lwrb_find(const lwrb_t* buff, const void* bts, lwrb_sz_t len, lwrb_sz_t start_offset, lwrb_sz_t* found_idx) {
|
||||||
|
lwrb_sz_t full, r, max_x;
|
||||||
|
uint8_t found = 0;
|
||||||
|
const uint8_t* needle = bts;
|
||||||
|
|
||||||
|
if (!BUF_IS_VALID(buff) || needle == NULL || len == 0 || found_idx == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*found_idx = 0;
|
||||||
|
|
||||||
|
full = lwrb_get_full(buff);
|
||||||
|
/* Verify initial conditions */
|
||||||
|
if (full < (len + start_offset)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Max number of for loops is buff_full - input_len - start_offset of buffer length */
|
||||||
|
max_x = full - len;
|
||||||
|
for (lwrb_sz_t skip_x = start_offset; !found && skip_x <= max_x; ++skip_x) {
|
||||||
|
found = 1; /* Found by default */
|
||||||
|
|
||||||
|
/* Prepare the starting point for reading */
|
||||||
|
r = buff->r + skip_x;
|
||||||
|
if (r >= buff->size) {
|
||||||
|
r -= buff->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search in the buffer */
|
||||||
|
for (lwrb_sz_t i = 0; i < len; ++i) {
|
||||||
|
if (buff->buff[r] != needle[i]) {
|
||||||
|
found = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (++r >= buff->size) {
|
||||||
|
r = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
|
*found_idx = skip_x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
#include "gl_state.h"
|
#include "gl_state.h"
|
||||||
#include "gl_img_process.h"
|
#include "gl_img_process.h"
|
||||||
#include "gl_common.h"
|
#include "gl_common.h"
|
||||||
#include "main.h"
|
|
||||||
#include "gl_handle_img.h"
|
#include "gl_handle_img.h"
|
||||||
#include "gl_transform_table.h"
|
#include "gl_transform_table.h"
|
||||||
#include "gl_get_corners.h"
|
#include "gl_get_corners.h"
|
||||||
|
|||||||
@@ -34,9 +34,12 @@
|
|||||||
********************************************************************************************************************/
|
********************************************************************************************************************/
|
||||||
|
|
||||||
#include "zf_common_headfile.h"
|
#include "zf_common_headfile.h"
|
||||||
|
#include "by_tiny_frame.h"
|
||||||
#include "by_button.h"
|
#include "by_button.h"
|
||||||
#include "by_buzzer.h"
|
#include "by_buzzer.h"
|
||||||
|
|
||||||
|
#include "by_tiny_frame_parse.h"
|
||||||
|
|
||||||
void NMI_Handler(void) __attribute__((interrupt()));
|
void NMI_Handler(void) __attribute__((interrupt()));
|
||||||
void HardFault_Handler(void) __attribute__((interrupt()));
|
void HardFault_Handler(void) __attribute__((interrupt()));
|
||||||
|
|
||||||
@@ -97,8 +100,11 @@ void USART3_IRQHandler(void)
|
|||||||
{
|
{
|
||||||
if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) {
|
if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) {
|
||||||
#if DEBUG_UART_USE_INTERRUPT // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> debug <20><><EFBFBD><EFBFBD><EFBFBD>ж<EFBFBD>
|
#if DEBUG_UART_USE_INTERRUPT // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> debug <20><><EFBFBD><EFBFBD><EFBFBD>ж<EFBFBD>
|
||||||
debug_interrupr_handler(); // <20><><EFBFBD><EFBFBD> debug <20><><EFBFBD>ڽ<EFBFBD><DABD>մ<EFBFBD><D5B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>ݻᱻ debug <20><><EFBFBD>λ<EFBFBD><CEBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȡ
|
// debug_interrupr_handler(); // <20><><EFBFBD><EFBFBD> debug <20><><EFBFBD>ڽ<EFBFBD><DABD>մ<EFBFBD><D5B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>ݻᱻ debug <20><><EFBFBD>λ<EFBFBD><CEBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȡ
|
||||||
#endif // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><DEB8><EFBFBD> DEBUG_UART_INDEX <20><><EFBFBD><EFBFBD><EFBFBD>δ<EFBFBD><CEB4><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD>ŵ<EFBFBD><C5B5><EFBFBD>Ӧ<EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD><EFBFBD>ж<EFBFBD>ȥ
|
#endif // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><DEB8><EFBFBD> DEBUG_UART_INDEX <20><><EFBFBD><EFBFBD><EFBFBD>δ<EFBFBD><CEB4><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD>ŵ<EFBFBD><C5B5><EFBFBD>Ӧ<EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD><EFBFBD>ж<EFBFBD>ȥ
|
||||||
|
uint8_t data_s = 0;
|
||||||
|
uart_query_byte(UART_3, &data_s);
|
||||||
|
by_tiny_frame_parse_uart_handle(data_s);
|
||||||
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
|
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
43
app/main.c
43
app/main.c
@@ -21,13 +21,17 @@
|
|||||||
* 许可证副本在 libraries 文件夹下 即该文件夹下的 LICENSE 文件
|
* 许可证副本在 libraries 文件夹下 即该文件夹下的 LICENSE 文件
|
||||||
* 欢迎各位使用并传播本程序 但修改内容时必须保留逐飞科技的版权声明(即本声明)
|
* 欢迎各位使用并传播本程序 但修改内容时必须保留逐飞科技的版权声明(即本声明)
|
||||||
********************************************************************************************************************/
|
********************************************************************************************************************/
|
||||||
|
#include "zf_common_headfile.h"
|
||||||
#include "gl_headfile.h"
|
#include "gl_headfile.h"
|
||||||
#include "./page/page.h"
|
#include "page.h"
|
||||||
|
#include "by_tiny_frame.h"
|
||||||
#include "by_buzzer.h"
|
#include "by_buzzer.h"
|
||||||
#include "by_led.h"
|
#include "by_led.h"
|
||||||
#include "jj_param.h"
|
#include "jj_param.h"
|
||||||
#include "jj_blueteeth.h"
|
#include "jj_blueteeth.h"
|
||||||
|
|
||||||
|
#include "by_tiny_frame_parse.h"
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -49,25 +53,30 @@ int main(void)
|
|||||||
// pit_ms_init(TIM6_PIT, 2);
|
// pit_ms_init(TIM6_PIT, 2);
|
||||||
// pit_ms_init(TIM1_PIT, 2);
|
// pit_ms_init(TIM1_PIT, 2);
|
||||||
|
|
||||||
|
by_tiny_frame_init();
|
||||||
|
printf("start running\r\n");
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
Page_Run();
|
Page_Run();
|
||||||
by_buzzer_run();
|
by_buzzer_run();
|
||||||
if (mt9v03x_finish_flag) {
|
by_tiny_frame_parse_run();
|
||||||
// 该操作消耗大概 1970 个 tick,折合约 110us
|
system_delay_ms(100);
|
||||||
memcpy(mt9v03x_image_copy[0], mt9v03x_image[0], (sizeof(mt9v03x_image_copy) / sizeof(uint8_t)));
|
// if (mt9v03x_finish_flag) {
|
||||||
// adaptiveThreshold((uint8_t*)mt9v03x_image_copy, (uint8_t*)mt9v03x_image_copy, 188, 120, 7, 17);
|
// // 该操作消耗大概 1970 个 tick,折合约 110us
|
||||||
// ips200_show_gray_image(0, 0, mt9v03x_image_copy[0], MT9V03X_W, MT9V03X_H, MT9V03X_W, MT9V03X_H, 0);
|
// memcpy(mt9v03x_image_copy[0], mt9v03x_image[0], (sizeof(mt9v03x_image_copy) / sizeof(uint8_t)));
|
||||||
mt9v03x_finish_flag = 0;
|
// // adaptiveThreshold((uint8_t*)mt9v03x_image_copy, (uint8_t*)mt9v03x_image_copy, 188, 120, 7, 17);
|
||||||
by_led_info_blink();
|
// // ips200_show_gray_image(0, 0, mt9v03x_image_copy[0], MT9V03X_W, MT9V03X_H, MT9V03X_W, MT9V03X_H, 0);
|
||||||
state_type = COMMON_STATE;
|
// mt9v03x_finish_flag = 0;
|
||||||
img_processing();
|
// by_led_info_blink();
|
||||||
get_corners();
|
// state_type = COMMON_STATE;
|
||||||
aim_distance = COMMON_AIM;
|
// img_processing();
|
||||||
tracking();
|
// get_corners();
|
||||||
ElementJudge();
|
// aim_distance = COMMON_AIM;
|
||||||
ElementRun();
|
// tracking();
|
||||||
MidLineTrack();
|
// ElementJudge();
|
||||||
}
|
// ElementRun();
|
||||||
|
// MidLineTrack();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef MAIN_H
|
|
||||||
#define MAIN_H
|
|
||||||
|
|
||||||
#include "zf_common_headfile.h"
|
|
||||||
|
|
||||||
#endif // MAIN_H
|
|
||||||
16
app/tiny_frame/by_tiny_frame.c
Normal file
16
app/tiny_frame/by_tiny_frame.c
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#include "by_tiny_frame.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "by_tiny_frame_parse.h"
|
||||||
|
#include "crc16.h"
|
||||||
|
#include "zf_common_headfile.h"
|
||||||
|
|
||||||
|
void by_tiny_frame_init(void)
|
||||||
|
{
|
||||||
|
uart_init(BY_TF_UART_INDEX, BY_TF_UART_BAUDRATE, BY_TF_UART_TX_PIN, BY_TF_UART_RX_PIN);
|
||||||
|
uart_rx_interrupt(BY_TF_UART_INDEX, ENABLE);
|
||||||
|
|
||||||
|
by_tiny_frame_parse_init();
|
||||||
|
}
|
||||||
8
app/tiny_frame/by_tiny_frame.h
Normal file
8
app/tiny_frame/by_tiny_frame.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef _BY_TINY_FRAME_H__
|
||||||
|
#define _BY_TINY_FRAME_H__
|
||||||
|
|
||||||
|
#include "by_tiny_frame_config.h"
|
||||||
|
|
||||||
|
extern void by_tiny_frame_init(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
11
app/tiny_frame/by_tiny_frame_config.h
Normal file
11
app/tiny_frame/by_tiny_frame_config.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef _BY_TINY_FRAME_CONFIG_H__
|
||||||
|
#define _BY_TINY_FRAME_CONFIG_H__
|
||||||
|
|
||||||
|
#define BY_TF_UART_TX_PIN (UART3_MAP0_TX_B10)
|
||||||
|
#define BY_TF_UART_RX_PIN (UART3_MAP0_RX_B11)
|
||||||
|
#define BY_TF_UART_INDEX (UART_3)
|
||||||
|
#define BY_TF_UART_BAUDRATE (115200)
|
||||||
|
|
||||||
|
#define BY_TF_PARSE_BUFFER_SIZE (50)
|
||||||
|
|
||||||
|
#endif
|
||||||
97
app/tiny_frame/by_tiny_frame_parse.c
Normal file
97
app/tiny_frame/by_tiny_frame_parse.c
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#include "by_tiny_frame_parse.h"
|
||||||
|
|
||||||
|
#include "crc16.h"
|
||||||
|
#include "lwrb.h"
|
||||||
|
|
||||||
|
lwrb_t lwrb_struct;
|
||||||
|
uint8_t buffer_rb[BY_TF_PARSE_BUFFER_SIZE];
|
||||||
|
uint8_t buffer_out;
|
||||||
|
by_tf_parse_frame_t frame_now;
|
||||||
|
|
||||||
|
void by_tiny_frame_parse_init(void)
|
||||||
|
{
|
||||||
|
lwrb_init(&lwrb_struct, buffer_rb, 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t by_tiny_frame_parse_listening(by_tf_parse_frame_t *frame_s, const uint8_t slave_id, const uint8_t buff)
|
||||||
|
{
|
||||||
|
|
||||||
|
static uint8_t cnt_s = 0;
|
||||||
|
static uint8_t cnt_rest_s = 0;
|
||||||
|
|
||||||
|
printf("%0.2X\r\n", buff);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if ((0 == cnt_s) && (((slave_id << 1) + 1) == buff)) {
|
||||||
|
memset(frame_s, 0, sizeof(*frame_s));
|
||||||
|
cnt_s = 1;
|
||||||
|
cnt_rest_s = 9;
|
||||||
|
frame_s->frame[0] = buff;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 <= cnt_s) {
|
||||||
|
frame_s->frame[cnt_s] = buff;
|
||||||
|
cnt_s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == --cnt_rest_s) {
|
||||||
|
cnt_s = 0;
|
||||||
|
|
||||||
|
frame_s->cmd = frame_s->frame[1];
|
||||||
|
frame_s->reg_addr |= ((uint16_t)frame_s->frame[2] << 8);
|
||||||
|
frame_s->reg_addr |= (uint16_t)frame_s->frame[3];
|
||||||
|
frame_s->data |= ((uint32_t)frame_s->frame[4] << 24);
|
||||||
|
frame_s->data |= ((uint32_t)frame_s->frame[5] << 16);
|
||||||
|
frame_s->data |= ((uint32_t)frame_s->frame[6] << 8);
|
||||||
|
frame_s->data |= (uint32_t)frame_s->frame[7];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void by_tiny_frame_parse_uart_handle(uint8_t buff)
|
||||||
|
{
|
||||||
|
lwrb_write(&lwrb_struct, &buff, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void by_tiny_frame_parse_run(void)
|
||||||
|
{
|
||||||
|
for (uint8_t i = 0; i < lwrb_get_full(&lwrb_struct); i++) {
|
||||||
|
|
||||||
|
if (!lwrb_read(&lwrb_struct, &buffer_out, 1)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!by_tiny_frame_parse_listening(&frame_now, 127, buffer_out)) {
|
||||||
|
if (!by_tiny_frame_parse_crc(&frame_now)) {
|
||||||
|
printf("frame parsed!\r\n");
|
||||||
|
}
|
||||||
|
// 解析帧
|
||||||
|
}
|
||||||
|
// if (!mp_cmd_parse_modbus_handle(data)) {
|
||||||
|
// mp_cmd_mb_parse(&mp_cmd_mb_now, &mp_cmd_parsed_now);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t by_tiny_frame_parse_crc(by_tf_parse_frame_t *frame_s)
|
||||||
|
{
|
||||||
|
uint16_t calc_crc_val = 0;
|
||||||
|
|
||||||
|
calc_crc_val = crc16_check(frame_s->frame, (sizeof(frame_s->frame) - 2));
|
||||||
|
|
||||||
|
printf("get: %0.2X", frame_s->crc_val);
|
||||||
|
printf("\r\n");
|
||||||
|
|
||||||
|
printf("cal: %0.2X", calc_crc_val);
|
||||||
|
printf("\r\n");
|
||||||
|
|
||||||
|
if ((frame_s->crc_val == calc_crc_val) || (frame_s->crc_val == 0xFFFF)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
25
app/tiny_frame/by_tiny_frame_parse.h
Normal file
25
app/tiny_frame/by_tiny_frame_parse.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef _BY_TINY_FRAME_PARSE_H__
|
||||||
|
#define _BY_TINY_FRAME_PARSE_H__
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "by_tiny_frame_config.h"
|
||||||
|
|
||||||
|
// 从机地址 (1b) - 功能码 (1b) - 寄存器地址 (2b) - 数据 (4b) - CRC(2b)
|
||||||
|
// 从机地址 (1b) 0-127, 最低位表示发送方,主机请求低位为 0,从机应答低位为 1
|
||||||
|
|
||||||
|
typedef struct by_tf_parse_frame_t {
|
||||||
|
uint8_t frame[10];
|
||||||
|
uint8_t cmd;
|
||||||
|
uint16_t reg_addr;
|
||||||
|
uint16_t crc_val;
|
||||||
|
uint32_t data;
|
||||||
|
} by_tf_parse_frame_t;
|
||||||
|
|
||||||
|
extern void by_tiny_frame_parse_init(void);
|
||||||
|
extern void by_tiny_frame_parse_uart_handle(uint8_t buff);
|
||||||
|
extern void by_tiny_frame_parse_run(void);
|
||||||
|
extern uint8_t by_tiny_frame_parse_crc(by_tf_parse_frame_t *frame_s);
|
||||||
|
|
||||||
|
#endif
|
||||||
0
app/tiny_frame/by_tiny_frame_read.c
Normal file
0
app/tiny_frame/by_tiny_frame_read.c
Normal file
6
app/tiny_frame/by_tiny_frame_read.h
Normal file
6
app/tiny_frame/by_tiny_frame_read.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef _BY_TINY_FRAME_READ_H__
|
||||||
|
#define _BY_TINY_FRAME_READ_H__
|
||||||
|
|
||||||
|
#define BY_TINY_FRAME_WRITE_CMD_CODE (0x03)
|
||||||
|
|
||||||
|
#endif
|
||||||
2
app/tiny_frame/by_tiny_frame_write.c
Normal file
2
app/tiny_frame/by_tiny_frame_write.c
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#include "by_tiny_frame_write.h"
|
||||||
|
|
||||||
6
app/tiny_frame/by_tiny_frame_write.h
Normal file
6
app/tiny_frame/by_tiny_frame_write.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef _BY_TINY_FRAME_WRITE_H__
|
||||||
|
#define _BY_TINY_FRAME_WRITE_H__
|
||||||
|
|
||||||
|
#define BY_TINY_FRAME_WRITE_CMD_CODE (0x06)
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user