Add the initial version icom(internal communication)

This commit is contained in:
Lamdonn 2026-03-15 23:06:47 +08:00
parent 2d13818a5a
commit af9120c9f8
6 changed files with 1324 additions and 0 deletions

86
doc/icom.en.md Normal file
View File

@ -0,0 +1,86 @@
# ICOM module documentation
## Overview
ICOM (Internal Communication) is a simple C language internal communication module, which is used to realize efficient bidirectional data communication in embedded systems. It provides a communication mechanism based on shared memory to support multi-channel data transmission and state management.
## Core features
- **Bidirectional communication** : Support for separate send and receive channels
- **State management** : Provides four channel states: initialized, idle, busy, and error
- **Data length control** : Supports dynamic data length setting and validation
- **Live detection** : Automatically detects the online state of the communication peer
- **Cache control** : Supports cache invalidation and writeback operations
- **Multi-channel support** : Multiple send and receive channels can be configured
## Architectural Design
### Communication protocol format
| offset | field | size | description |
| :----: | :--: | :--: | :--------- |
| 0 | Flag | 4 | Channel status flag |
| 4 | Tcnt | 4 | Send channel count |
| 8 | Rcnt | 4 | Receive channel count |
| 12 | Dlen | 4 | data length |
| 16 | Data | N | actual data |
### State Machine design
- **INIT** : The initial state
- **BUSY** : Busy (in data transfer)
- **IDLE** : Idle state (transferable)
- **ERROR** : An error state
## API explained in detail
### Initialization functions
```c
int icom_init(ICOM *icom);
```
Initialize the ICOM module and configure the sending and receiving channels.
### Task handlers
```c
int icom_task(ICOM *icom);
```
Handle the periodic tasks of the ICOM module, including online state detection and data reception processing.
### Data sending function
```c
int icom_transmit(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t length);
```
Send data over the specified channel.
### Data receiving functions
```c
int icom_receive(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t *length);
```
Handle the periodic tasks of the ICOM module, including online state detection and data reception processing. Receives data from the specified channel.
### Online presence checks
```c
int icom_txchannel_online(ICOM *icom, uint16_t channel, uint8_t *online);
int icom_rxchannel_online(ICOM *icom, uint16_t channel, uint8_t *online);
```
Check the online status of the send/receive channel.
## Instructions
1. Initialize the ICOM struct and configure the channel
2. Call icom_init() for initialization
3. Periodic calls to icom_task() for state management and data reception processing
4. Use icom_transmit() to send data
5. Use icom_receive() to receive data
## Advanced features
* Manual receive mode: You can manually check the receive status without using callbacks
* Cache control: Cache control via 'inv' and 'wbinv' callbacks
* Private data: Each channel maintains 4 private data words for extended functionality
## Use cases
* Inter-Processor Communication (IPC)
* Data exchange between multiple cores
* Modular system component communication
* Embedded systems that require shared memory communication

86
doc/icom.md Normal file
View File

@ -0,0 +1,86 @@
# ICOM 模块文档
## 概述
ICOM (Internal Communication) 是一个简单的C语言内部通信模块用于在嵌入式系统中实现高效的双向数据通信。它提供了基于共享内存的通信机制支持多通道数据传输和状态管理。
## 核心特性
- **双向通信**:支持独立的发送和接收通道
- **状态管理**:提供初始化、空闲、忙碌、错误四种通道状态
- **数据长度控制**:支持动态数据长度设置和校验
- **在线检测**:自动监测通信对端在线状态
- **缓存控制**:支持缓存无效化和回写操作
- **多通道支持**:可配置多个发送和接收通道
## 架构设计
### 通信协议格式
| 偏移量 | 字段 | 大小 | 描述 |
| :----: | :---: | :--: | :--------- |
| 0 | Flag | 4 | 通道状态标志 |
| 4 | Tcnt | 4 | 发送通道计数 |
| 8 | Rcnt | 4 | 接收通道计数 |
| 12 | Dlen | 4 | 数据长度 |
| 16 | Data | N | 实际数据 |
### 状态机设计
- **INIT** : 初始化状态
- **BUSY** : 忙碌状态(数据传输中)
- **IDLE** : 空闲状态(可传输)
- **ERROR** : 错误状态
## API接口详解
### 初始化函数
```c
int icom_init(ICOM *icom);
```
初始化ICOM模块配置发送和接收通道。
### 任务处理函数
```c
int icom_task(ICOM *icom);
```
处理ICOM模块的周期性任务包括在线状态检测和数据接收处理。
### 数据发送函数
```c
int icom_transmit(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t length);
```
通过指定通道发送数据。
### 数据接收函数
```c
int icom_receive(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t *length);
```
处理ICOM模块的周期性任务包括在线状态检测和数据接收处理。从指定通道接收数据。
### 在线状态检查
```c
int icom_txchannel_online(ICOM *icom, uint16_t channel, uint8_t *online);
int icom_rxchannel_online(ICOM *icom, uint16_t channel, uint8_t *online);
```
检查发送/接收通道的在线状态。
## 使用指南
1. 初始化ICOM结构体并配置通道
2. 调用icom_init()进行初始化
3. 周期性调用icom_task()
4. 使用icom_transmit()发送数据
5. 使用icom_receive()接收数据
## 高级功能
* 手动接收模式:可以不使用回调函数,手动检查接收状态
* 缓存控制:通过 `inv``wbinv` 回调函数实现缓存控制
* 私有数据每个通道维护4个私有数据字用于扩展功能
## 应用场景
* 处理器间通信(IPC)
* 多核间数据交换
* 模块化系统组件通信
* 需要共享内存通信的嵌入式系统

View File

@ -0,0 +1,631 @@
/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file icom.c
* \unit icom
* \brief This is a simple internal communication module for C language
* \author Lamdonn
* \version v0.1.0
* \license GPL-2.0
* \copyright Copyright (C) 2026 Lamdonn.
********************************************************************************************************/
#include "icom.h"
/**
* \brief: ICOM header field
* \note: This size is used to store the flag, transmit channel count, receive channel count, and data length of the channel
*/
#define ICOM_FLAG_SIZE (4) /**< ICOM flag size */
#define ICOM_TCNT_SIZE (4) /**< ICOM transmit channel count size */
#define ICOM_RCNT_SIZE (4) /**< ICOM receive channel count size */
#define ICOM_DLEN_SIZE (4) /**< ICOM data length size */
/**
* \brief: ICOM header field index
* \note: This index is used to access the flag, transmit channel count, receive channel count, and data length of the channel
*/
#define ICOM_FLAG_INDEX (0)
#define ICOM_TCNT_INDEX (ICOM_FLAG_INDEX + ICOM_FLAG_SIZE)
#define ICOM_RCNT_INDEX (ICOM_TCNT_INDEX + ICOM_TCNT_SIZE)
#define ICOM_DLEN_INDEX (ICOM_RCNT_INDEX + ICOM_RCNT_SIZE)
#define ICOM_DATA_INDEX (ICOM_DLEN_INDEX + ICOM_DLEN_SIZE)
/**
* \brief: ICOM header size
* \note:
* | Offset | Field | Size | Description |
* | :----: | :---: | :--: | :--------- |
* | 0 | Flag | 4 | Channel flag |
* | 4 | Tcnt | 4 | Transmit channel count |
* | 8 | Rcnt | 4 | Receive channel count |
* | 12 | Dlen | 4 | Data length |
*/
#define ICOM_HEAD_SIZE (ICOM_FLAG_SIZE + ICOM_TCNT_SIZE + ICOM_RCNT_SIZE + ICOM_DLEN_SIZE)
/**
* \brief: ICOM channel flag
* \note: This flag is used to indicate the status of the channel
*/
#define ICOM_F_INIT ((uint8_t)0x12) /**< ICOM channel flag: initialized */
#define ICOM_F_BUSY ((uint8_t)0x34) /**< ICOM channel flag: busy */
#define ICOM_F_IDLE ((uint8_t)0x56) /**< ICOM channel flag: idle */
#define ICOM_F_ERROR ((uint8_t)0x78) /**< ICOM channel flag: error */
#define getTxBase(i) (icom->txconfig[i].base) /**< ICOM transmit channel base address */
#define getTxSize(i) (icom->txconfig[i].size) /**< ICOM transmit channel size */
#define getTxFlag(i) (getTxBase(i)[ICOM_FLAG_INDEX]) /**< ICOM transmit channel flag */
#define getTxDlen(i) (getTxBase(i)[ICOM_DLEN_INDEX]) /**< ICOM transmit channel data length */
#define getTxData(i) (getTxBase(i)[ICOM_DATA_INDEX]) /**< ICOM transmit channel data */
#define getTxTcnt(i) (getTxBase(i)[ICOM_TCNT_INDEX]) /**< ICOM transmit channel count */
#define getTxRcnt(i) (getTxBase(i)[ICOM_RCNT_INDEX]) /**< ICOM transmit channel receive count */
#define getRxBase(i) (icom->rxconfig[i].base) /**< ICOM receive channel base address */
#define getRxSize(i) (icom->rxconfig[i].size) /**< ICOM receive channel size */
#define getRxFlag(i) (getRxBase(i)[ICOM_FLAG_INDEX]) /**< ICOM receive channel flag */
#define getRxDlen(i) (getRxBase(i)[ICOM_DLEN_INDEX]) /**< ICOM receive channel data length */
#define getRxData(i) (getRxBase(i)[ICOM_DATA_INDEX]) /**< ICOM receive channel data */
#define getRxTcnt(i) (getRxBase(i)[ICOM_TCNT_INDEX]) /**< ICOM receive channel count */
#define getRxRcnt(i) (getRxBase(i)[ICOM_RCNT_INDEX]) /**< ICOM receive channel receive count */
#define getTxPrivate(i) (icom->txconfig[i].private) /**< ICOM transmit channel private data */
#define getRxPrivate(i) (icom->rxconfig[i].private) /**< ICOM receive channel private data */
#define getPrivateOnline(p) ((p)[0]) /**< ICOM private data: online */
#define getPrivateOffCnt(p) ((p)[1]) /**< ICOM private data: offline count */
#define getPrivatePCount(p) ((p)[2]) /**< ICOM private data: process count */
#define getPrivateCCount(p) ((p)[3]) /**< ICOM private data: complete count */
/**
* \brief: ICOM cache invalidate function
* \param addr: Cache address
* \param size: Cache size
*/
#define Cache_Inv(addr, size) do { if (icom->inv) (icom->inv)((addr), (size)); } while (0)
/**
* \brief: ICOM cache write-back invalidate function
* \param addr: Cache address
* \param size: Cache size
*/
#define Cache_wbInv(addr, size) do { if (icom->wbinv) (icom->wbinv)((addr), (size)); } while (0)
/**
* \brief: ICOM transmitter ready function
* \param icom: ICOM handle
* \param channel: Channel number
* \return: 1 means ready, 0 means not ready
*/
static int icom_transmitter_ready(ICOM *icom, uint16_t channel)
{
int ret = 0;
Cache_Inv(&getRxFlag(channel), ICOM_FLAG_SIZE);
switch (getRxFlag(channel))
{
case ICOM_F_INIT:
{
getRxFlag(channel) = ICOM_F_IDLE;
Cache_wbInv(&getRxFlag(channel), ICOM_FLAG_SIZE);
ret = 1;
} break;
case ICOM_F_IDLE:
case ICOM_F_BUSY:
{
ret = 1;
} break;
default:
{
ret = 0;
} break;
}
return ret;
}
/**
* \brief: ICOM set data length function
* \param icom: ICOM handle
* \param channel: Channel number
* \param length: Data length
*/
static void icom_set_data_length(ICOM *icom, uint16_t channel, uint32_t length)
{
/* Data length is stored in little-endian format */
getTxBase(channel)[ICOM_DLEN_INDEX] = length & 0xFF;
getTxBase(channel)[ICOM_DLEN_INDEX + 1] = (length >> 8) & 0xFF;
getTxBase(channel)[ICOM_DLEN_INDEX + 2] = (length >> 16) & 0xFF;
getTxBase(channel)[ICOM_DLEN_INDEX + 3] = (length >> 24) & 0xFF;
Cache_wbInv(&(getTxBase(channel)[ICOM_DLEN_INDEX]), ICOM_DLEN_SIZE);
}
/**
* \brief: ICOM get data length function
* \param icom: ICOM handle
* \param channel: Channel number
* \return: Data length
*/
static uint32_t icom_get_data_length(ICOM *icom, uint16_t channel)
{
uint32_t length = 0;
Cache_Inv(&(getRxBase(channel)[ICOM_DLEN_INDEX]), ICOM_DLEN_SIZE);
/* Data length is stored in little-endian format */
length = (uint32_t)((getRxBase(channel)[ICOM_DLEN_INDEX]) |
(getRxBase(channel)[ICOM_DLEN_INDEX + 1] << 8) |
(getRxBase(channel)[ICOM_DLEN_INDEX + 2] << 16) |
(getRxBase(channel)[ICOM_DLEN_INDEX + 3] << 24));
return length;
}
/**
* \brief: ICOM control online function
* \param icom: ICOM handle
* \param private: Private data
* \param count: Process count
* \note: If process count is equal to complete count, then set online to 1.
* If offline count is less than 5, then set offline count to 1.
* Otherwise, set online to 0.
* If online is 1, then set offline count to 0.
* If offline count is greater than 5, then set online to 0.
*/
static void icom_control_online(ICOM *icom, uint32_t *private, uint32_t count)
{
getPrivateCCount(private) = count;
if (getPrivateCCount(private) == getPrivatePCount(private))
{
if (getPrivateOffCnt(private) <= 5)
{
getPrivateOffCnt(private)++;
}
/* If offline count is greater than 5, then set online to 0 */
else
{
getPrivateOnline(private) = 0;
}
}
else
{
getPrivateOnline(private) = 1;
getPrivateOffCnt(private) = 0;
}
getPrivatePCount(private) = getPrivateCCount(private);
}
/**
* \brief: ICOM online handle function
* \param icom: ICOM handle
* \note: If online is 1, then increment transmitter count.
* If offline count is greater than 5, then set online to 0.
*/
static void icom_online_handle(ICOM *icom)
{
uint16_t channel;
/* Handle tx channal */
if (icom->txconfig && icom->txcount > 0)
{
for (channel = 0; channel < icom->txcount; channel++)
{
Cache_Inv(&getTxTcnt(channel), ICOM_TCNT_SIZE);
/* Increase tx count to indicate tx channal is online */
(*((uint32_t *)(&getTxTcnt(channel))))++;
Cache_wbInv(&getTxTcnt(channel), ICOM_TCNT_SIZE);
Cache_Inv(&getTxRcnt(channel), ICOM_RCNT_SIZE);
/* Control online status */
icom_control_online(icom, getTxPrivate(channel), (*((uint32_t *)&getTxRcnt(channel))));
}
}
/* Handle rx channal */
if (icom->rxconfig && icom->rxcount > 0)
{
for (channel = 0; channel < icom->rxcount; channel++)
{
Cache_Inv(&getRxRcnt(channel), ICOM_RCNT_SIZE);
/* Increase rx count to indicate rx channal is online */
(*((uint32_t *)(&getRxRcnt(channel))))++;
Cache_wbInv(&getRxRcnt(channel), ICOM_RCNT_SIZE);
Cache_Inv(&getRxTcnt(channel), ICOM_TCNT_SIZE);
/* Control online status */
icom_control_online(icom, getRxPrivate(channel), (*((uint32_t *)&getRxTcnt(channel))));
}
}
}
/**
* \brief: ICOM rxdata handle function
* \param icom: ICOM handle
* \note: If rx channal is ready, then check data length.
* If data length is valid, then call callback function.
* Otherwise, set rx channal to idle.
*/
static void icom_rxdata_handle(ICOM *icom)
{
uint16_t channel;
uint32_t length = 0;
/* Handle Rx */
if (icom->rxconfig && icom->rxcount > 0)
{
for (channel = 0; channel < icom->rxcount; channel++)
{
/* Check rx channal is ready */
if (icom_transmitter_ready(icom, channel))
{
Cache_Inv(&getRxFlag(channel), ICOM_FLAG_SIZE);
/* Check rx channal is busy.
* Indicate that rx channal is ready to receive data.
*/
if (getRxFlag(channel) == ICOM_F_BUSY)
{
/* Get data length and check data length is valid */
length = icom_get_data_length(icom, channel);
if (length <= getRxSize(channel) - ICOM_HEAD_SIZE)
{
/* Call callback function */
if (icom->rxconfig[channel].callback)
{
Cache_Inv(&getRxData(channel), length);
icom->rxconfig[channel].callback(channel, &getRxData(channel), length);
/* Set rx channal to idle */
getRxFlag(channel) = ICOM_F_IDLE;
Cache_wbInv(&getRxFlag(channel), ICOM_FLAG_SIZE);
}
else /* Receive manual */
{
}
}
}
}
}
}
}
/**
* \brief: ICOM initialize function
* \param icom: ICOM handle
* \return: ICOM error code
*/
int icom_init(ICOM *icom)
{
uint16_t channel;
/* Input value validity check */
if (!icom) return ICOM_E_IHANDLE;
if (icom->init) return ICOM_E_OK;
/* Initialize tx channal */
if (icom->txconfig && icom->txcount > 0)
{
for (channel = 0; channel < icom->txcount; channel++)
{
if (getTxBase(channel) != NULL)
{
/* Initialize tx channal flag */
getTxFlag(channel) = ICOM_F_INIT;
Cache_wbInv(&getTxFlag(channel), ICOM_FLAG_SIZE);
}
/* Initialize tx channal private data */
memset(getTxPrivate(channel), 0, sizeof(getTxPrivate(channel)));
/* Initialize tx channal callback function, default is NULL */
icom->txconfig[channel].callback = NULL;
}
}
/* Initialize rx channal */
if (icom->rxconfig && icom->rxcount > 0)
{
for (channel = 0; channel < icom->rxcount; channel++)
{
if (getRxBase(channel) != NULL)
{
/* Initialize rx channal flag */
icom_transmitter_ready(icom, channel);
}
/* Initialize rx channal private data */
memset(getRxPrivate(channel), 0, sizeof(getRxPrivate(channel)));
}
}
/* ICOM controller hs been initialized. */
icom->init = 1;
return ICOM_E_OK;
}
/**
* \brief: ICOM task function
* \param icom: ICOM handle
* \return: ICOM error code
*/
int icom_task(ICOM *icom)
{
/* Input value validity check */
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
/* Handle online status */
icom_online_handle(icom);
/* Handle rxdata */
icom_rxdata_handle(icom);
return ICOM_E_OK;
}
/**
* \brief: ICOM transmit getbuffer function
* \param icom: ICOM handle
* \param channel: Transmit channal index
* \param buffer: Pointer to store data buffer
* \param length: Pointer to store data length
* \return: ICOM error code
*/
int icom_transmit_getbuffer(ICOM *icom, uint16_t channel, uint8_t **buffer, uint32_t *length)
{
/* Input value validity check */
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->txcount) return ICOM_E_IOUT;
if (!getTxBase(channel)) return ICOM_E_NBASE;
if (!buffer) return ICOM_E_IBUFFER;
/* Get transmit buffer */
*buffer = &getTxData(channel);
/* Get data length */
if (length) *length = getTxSize(channel) - ICOM_HEAD_SIZE;
return ICOM_E_OK;
}
/**
* \brief: ICOM transmit trigger function
* \param icom: ICOM handle
* \param channel: Transmit channal index
* \param length: Data length
* \note: Data length must be less than or equal to the transmit buffer size minus ICOM_HEAD_SIZE
* \return: ICOM error code
*/
int icom_transmit_trigger(ICOM *icom, uint16_t channel, uint32_t length)
{
/* Input value validity check */
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->txcount) return ICOM_E_IOUT;
if (!getTxBase(channel)) return ICOM_E_NBASE;
if (length + ICOM_HEAD_SIZE > getTxSize(channel)) return ICOM_E_LOUT;
if (getTxFlag(channel) != ICOM_F_IDLE) return ICOM_E_BUSY;
/* Set data length */
icom_set_data_length(icom, channel, length);
/* Set tx channal to busy, need rx object receive data and set to ICOM_F_IDLE */
getTxFlag(channel) = ICOM_F_BUSY;
Cache_wbInv(&getTxFlag(channel), ICOM_FLAG_SIZE);
return ICOM_E_OK;
}
/**
* \brief: ICOM transmit function
* \param icom: ICOM handle
* \param channel: Transmit channal index
* \param data: Pointer to data buffer
* \param length: Data length
* \note: Data length must be less than or equal to the transmit buffer size minus ICOM_HEAD_SIZE
* \return: ICOM error code
*/
int icom_transmit(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t length)
{
int ret = ICOM_E_OK;
uint8_t *buffer = NULL;
uint32_t blength = 0;
uint32_t i = 0;
/* Get transmit buffer */
ret = icom_transmit_getbuffer(icom, channel, &buffer, &blength);
if (ICOM_E_OK != ret) return ret;
if (length > blength) return ICOM_E_LOUT;
/* Copy data to transmit buffer */
for (i = 0; i < length; i++)
{
buffer[i] = data[i];
}
Cache_wbInv(buffer, length);
/* Trigger transmit */
ret = icom_transmit_trigger(icom, channel, length);
return ret;
}
/**
* \brief: ICOM receive getbuffer function
* \param icom: ICOM handle
* \param channel: Receive channal index
* \param buffer: Pointer to store data buffer
* \param length: Pointer to store data length
* \return: ICOM error code
*/
int icom_receive_getbuffer(ICOM *icom, uint16_t channel, uint8_t **buffer, uint32_t *length)
{
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->txcount) return ICOM_E_IOUT;
if (!getRxBase(channel)) return ICOM_E_NBASE;
if (!buffer) return ICOM_E_IBUFFER;
*buffer = &getRxData(channel);
if (length) *length = getRxSize(channel) - ICOM_HEAD_SIZE;
return ICOM_E_OK;
}
/**
* \brief: ICOM receive indicate function
* \param icom: ICOM handle
* \param channel: Receive channal index
* \param length: Pointer to store data length
* \return: ICOM error code
*/
int icom_receive_indicate(ICOM *icom, uint16_t channel, uint32_t *length)
{
/* Input value validity check */
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->rxcount) return ICOM_E_IOUT;
if (!getRxBase(channel)) return ICOM_E_NBASE;
/* Check if the rx channal is ready */
if (icom_transmitter_ready(icom, channel))
{
Cache_Inv(&getRxFlag(channel), ICOM_FLAG_SIZE);
if (getRxFlag(channel) == ICOM_F_BUSY)
{
/* Get data length */
if (length) *length = icom_get_data_length(icom, channel);
}
else
{
return ICOM_E_NRECEIVE;
}
}
else
{
return ICOM_E_NRECEIVE;
}
return ICOM_E_OK;
}
/**
* \brief: ICOM receive end function
* \param icom: ICOM handle
* \param channel: Receive channal index
* \return: ICOM error code
*/
int icom_receive_end(ICOM *icom, uint16_t channel)
{
/* Input value validity check */
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->rxcount) return ICOM_E_IOUT;
/* Check if the rx channal is ready */
if (icom_transmitter_ready(icom, channel))
{
Cache_Inv(&getRxFlag(channel), ICOM_FLAG_SIZE);
if (getRxFlag(channel) == ICOM_F_BUSY)
{
/* Set rx channal to idle */
getRxFlag(channel) = ICOM_F_IDLE;
}
else
{
return ICOM_E_NRECEIVE;
}
}
else
{
return ICOM_E_NRECEIVE;
}
return ICOM_E_OK;
}
/**
* \brief: ICOM receive function
* \param icom: ICOM handle
* \param channel: Receive channal index
* \param data: Pointer to data buffer
* \param length: Pointer to store data length
* \note: Data length must be less than or equal to the receive buffer size minus ICOM_HEAD_SIZE
* \return: ICOM error code
*/
int icom_receive(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t *length)
{
int ret = ICOM_E_OK;
uint8_t *buffer = NULL;
uint32_t rxlength = 0;
uint32_t blength = 0;
uint32_t ilength = 0;
uint32_t i = 0;
/* Input value validity check */
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->rxcount) return ICOM_E_IOUT;
if (!getRxBase(channel)) return ICOM_E_NBASE;
if (!data) return ICOM_E_IBUFFER;
if (!length) return ICOM_E_ILENGTH;
ilength = *length;
/* Determine if there is any data to receive */
ret = icom_receive_indicate(icom, channel, &rxlength);
if (ICOM_E_OK != ret) return ret;
/* Get receive buffer */
ret = icom_receive_getbuffer(icom, channel, &buffer, &blength);
if (ICOM_E_OK != ret) return ret;
if (rxlength > blength) return ICOM_E_LOUT;
/* Copy data from receive buffer */
for (i = 0; i < rxlength; i++)
{
data[i] = buffer[i];
}
/* End receive */
icom_receive_end(icom, channel);
*length = rxlength;
return ICOM_E_OK;
}
/**
* \brief: ICOM transmit channal online check function
* \param icom: ICOM handle
* \param channel: Transmit channal index
* \param online: Pointer to store online status
* \return: ICOM error code
*/
int icom_txchannel_online(ICOM *icom, uint16_t channel, uint8_t *online)
{
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->txcount) return ICOM_E_IOUT;
if (!online) return ICOM_E_IBUFFER;
*online = getPrivateOnline(getTxPrivate(channel));
return ICOM_E_OK;
}
/**
* \brief: ICOM receive channal online check function
* \param icom: ICOM handle
* \param channel: Receive channal index
* \param online: Pointer to store online status
* \return: ICOM error code
*/
int icom_rxchannel_online(ICOM *icom, uint16_t channel, uint8_t *online)
{
if (!icom) return ICOM_E_IHANDLE;
if (!icom->init) return ICOM_E_NINIT;
if (channel >= icom->rxcount) return ICOM_E_IOUT;
if (!online) return ICOM_E_IBUFFER;
*online = getPrivateOnline(getRxPrivate(channel));
return ICOM_E_OK;
}

View File

@ -0,0 +1,193 @@
/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file icom.h
* \unit icom
* \brief This is a simple internal communication module for C language
* \author Lamdonn
* \version v0.1.0
* \license GPL-2.0
* \copyright Copyright (C) 2026 Lamdonn.
********************************************************************************************************/
#ifndef __icom_H
#define __icom_H
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/* Version infomation */
#define ICOM_V_MAJOR 0
#define ICOM_V_MINOR 1
#define ICOM_V_PATCH 0
/**
* \brief: ICOM error code
* \note: 0 means success, negative value means error
*/
#define ICOM_E_OK (0) /* OK, no error */
#define ICOM_E_IHANDLE (-1) /* Invalid icom handle */
#define ICOM_E_NINIT (-2) /* Not initialized */
#define ICOM_E_IOUT (-3) /* Iterator out of range */
#define ICOM_E_NBASE (-4) /* NULL base address */
#define ICOM_E_LOUT (-5) /* Length out of range */
#define ICOM_E_BUSY (-6) /* Current channel is busy */
#define ICOM_E_IBUFFER (-7) /* Invalid buffer */
#define ICOM_E_ILENGTH (-8) /* Invalid length */
#define ICOM_E_NRECEIVE (-9) /* No data received */
/**
* \brief: ICOM receive callback function
* \param channel: Channel number
* \param data: Data buffer
* \param length: Data length
* \return: ICOM error code
*/
typedef int (*icom_callback_t)(uint16_t channel, uint8_t *data, uint32_t length);
/**
* \brief: ICOM cache invalidate function
* \param address: Cache address
* \param size: Cache size
*/
typedef void (*icom_cache_inv_t)(uint8_t *address, uint32_t size);
/**
* \brief: ICOM cache write-back invalidate function
* \param address: Cache address
* \param size: Cache size
*/
typedef void (*icom_cache_wbinv_t)(uint8_t *address, uint32_t size);
/**
* \brief: ICOM channel configuration structure
* \note: This structure is used to configure the ICOM channel
*/
typedef struct
{
uint8_t *base; /**< Base address of the channel */
uint32_t size; /**< Size of the channel */
icom_callback_t callback; /**< Callback function */
uint32_t private[4]; /**< Private data */
} ICOM_CHNCFG;
/**
* \brief: ICOM structure
* \note: This structure is used to manage the ICOM module
*/
typedef struct
{
uint8_t init; /**< Initialize flag */
ICOM_CHNCFG *txconfig; /**< Transmit channel configuration */
ICOM_CHNCFG *rxconfig; /**< Receive channel configuration */
uint16_t txcount; /**< Transmit channel count */
uint16_t rxcount; /**< Receive channel count */
icom_cache_inv_t inv; /**< Cache invalidate function */
icom_cache_wbinv_t wbinv; /**< Cache write-back invalidate function */
} ICOM;
/**
* \brief: ICOM initialize function
* \param icom: ICOM handle
* \return: ICOM error code
*/
int icom_init(ICOM *icom);
/**
* \brief: ICOM task function
* \param icom: ICOM handle
* \return: ICOM error code
*/
int icom_task(ICOM *icom);
/**
* \brief: ICOM transmit getbuffer function
* \param icom: ICOM handle
* \param channel: Transmit channal index
* \param buffer: Pointer to store data buffer
* \param length: Pointer to store data length
* \return: ICOM error code
*/
int icom_transmit_getbuffer(ICOM *icom, uint16_t channel, uint8_t **buffer, uint32_t *length);
/**
* \brief: ICOM transmit trigger function
* \param icom: ICOM handle
* \param channel: Transmit channal index
* \param length: Data length
* \note: Data length must be less than or equal to the transmit buffer size minus ICOM_HEAD_SIZE
* \return: ICOM error code
*/
int icom_transmit_trigger(ICOM *icom, uint16_t channel, uint32_t length);
/**
* \brief: ICOM transmit function
* \param icom: ICOM handle
* \param channel: Transmit channal index
* \param data: Pointer to data buffer
* \param length: Data length
* \note: Data length must be less than or equal to the transmit buffer size minus ICOM_HEAD_SIZE
* \return: ICOM error code
*/
int icom_transmit(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t length);
/**
* \brief: ICOM receive getbuffer function
* \param icom: ICOM handle
* \param channel: Receive channal index
* \param buffer: Pointer to store data buffer
* \param length: Pointer to store data length
* \return: ICOM error code
*/
int icom_receive_getbuffer(ICOM *icom, uint16_t channel, uint8_t **buffer, uint32_t *length);
/**
* \brief: ICOM receive indicate function
* \param icom: ICOM handle
* \param channel: Receive channal index
* \param length: Pointer to store data length
* \return: ICOM error code
*/
int icom_receive_indicate(ICOM *icom, uint16_t channel, uint32_t *length);
/**
* \brief: ICOM receive end function
* \param icom: ICOM handle
* \param channel: Receive channal index
* \return: ICOM error code
*/
int icom_receive_end(ICOM *icom, uint16_t channel);
/**
* \brief: ICOM receive function
* \param icom: ICOM handle
* \param channel: Receive channal index
* \param data: Pointer to data buffer
* \param length: Pointer to store data length
* \note: Data length must be less than or equal to the receive buffer size minus ICOM_HEAD_SIZE
* \return: ICOM error code
*/
int icom_receive(ICOM *icom, uint16_t channel, uint8_t *data, uint32_t *length);
/**
* \brief: ICOM transmit channal online check function
* \param icom: ICOM handle
* \param channel: Transmit channal index
* \param online: Pointer to store online status
* \return: ICOM error code
*/
int icom_txchannel_online(ICOM *icom, uint16_t channel, uint8_t *online);
/**
* \brief: ICOM receive channal online check function
* \param icom: ICOM handle
* \param channel: Receive channal index
* \param online: Pointer to store online status
* \return: ICOM error code
*/
int icom_rxchannel_online(ICOM *icom, uint16_t channel, uint8_t *online);
#endif

View File

@ -45,6 +45,7 @@ TEST_LIST += romt
TEST_LIST += cant TEST_LIST += cant
TEST_LIST += slup TEST_LIST += slup
# TEST_LIST += cpul # TEST_LIST += cpul
TEST_LIST += icom
TEST_LIST += date TEST_LIST += date
TEST_LIST += unitt TEST_LIST += unitt
TEST_LIST += coroutine TEST_LIST += coroutine

327
test/test_icom.c Normal file
View File

@ -0,0 +1,327 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(TEST_TARGET_icom)
#include <varch/command.h>
#include <varch/unitt.h>
#include <varch/icom.h>
#else
#include "init.h"
#include "command.h"
#include "unitt.h"
#include "kern.h"
#include "icom.h"
#endif
/************************************************************************************/
/************************************* Unit Test ************************************/
/************************************************************************************/
// #define EXIT_TEST
extern uint64_t unitt_clock(void);
static int test_0(void)
{
for (int i = 0; i < 100; i++)
{
if (0)
{
#if defined (EXIT_TEST)
exit(0);
#endif
return UNITT_E_FAIL;
}
}
return UNITT_E_OK;
}
static void unitt_task(void)
{
static UNITT_TCASE rand_tests[] = {
UNITT_TCASE(test_0),
// UNITT_TCASE(test_1),
// UNITT_TCASE(test_2),
};
static UNITT suites[] = {
{ "icom suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock },
};
UNITT_EXE(suites);
}
/************************************************************************************/
/************************************* Base Test ************************************/
/************************************************************************************/
static uint8_t icomBuffer0[1024] = {0};
static uint8_t icomBuffer1[1024] = {0};
static int dev0_rx_callback(uint16_t channel, uint8_t *data, uint32_t length);
static int dev1_rx_callback(uint16_t channel, uint8_t *data, uint32_t length);
static ICOM dev0ICOM = {
.init = 0,
.txconfig = (ICOM_CHNCFG[1]){
{
.base = icomBuffer0,
.size = sizeof(icomBuffer0),
.callback = NULL,
},
},
.rxconfig = (ICOM_CHNCFG[1]){
{
.base = icomBuffer1,
.size = sizeof(icomBuffer1),
.callback = dev0_rx_callback,
},
},
.txcount = 1,
.rxcount = 1,
.inv = NULL,
.wbinv = NULL,
};
static ICOM dev1ICOM = {
.init = 0,
.txconfig = (ICOM_CHNCFG[1]){
{
.base = icomBuffer1,
.size = sizeof(icomBuffer1),
.callback = NULL,
},
},
.rxconfig = (ICOM_CHNCFG[1]){
{
.base = icomBuffer0,
.size = sizeof(icomBuffer0),
// .callback = dev1_rx_callback,
.callback = NULL,
},
},
.txcount = 1,
.rxcount = 1,
.inv = NULL,
.wbinv = NULL,
};
static int dev0_rx_callback(uint16_t channel, uint8_t *data, uint32_t length)
{
printf("[%d]: %s\r\n", length, data);
return 0;
}
static int dev1_rx_callback(uint16_t channel, uint8_t *data, uint32_t length)
{
printf("[%d]: %s\r\n", length, data);
// printf("[%d]\r\n", length);
// for (int i = 0; i < length; i++)
// {
// printf("%02x ", data[i]);
// }
// printf("\r\n");
icom_transmit(&dev1ICOM, 0, "Copy that!", 11);
return 0;
}
static void dev0_task(void);
static void dev1_task(void);
static void dev0_task(void)
{
static uint8_t init = 0;
static uint32_t count = 0;
if (!init)
{
icom_init(&dev0ICOM);
init = 1;
}
count += 10;
if (count >= 25200000) count = 0;
if (count % 10 == 0)
{
icom_task(&dev0ICOM);
}
if (count % 1000 == 0)
{
uint8_t txonline = 0, rxonline = 0;
icom_txchannel_online(&dev0ICOM, 0, &txonline);
icom_rxchannel_online(&dev0ICOM, 0, &rxonline);
printf("online %d,%d\r\n", txonline, rxonline);
icom_transmit(&dev0ICOM, 0, "Hello dev1", 11);
static int time = 0;
if (time >= 3)
{
task_create(10, dev1_task);
}
else
{
time++;
}
}
}
static void dev1_task(void)
{
static uint8_t init = 0;
static uint32_t count = 0;
if (!init)
{
icom_init(&dev1ICOM);
init = 1;
}
count += 10;
if (count >= 25200000) count = 0;
char rxBuffer[1024];
uint32_t length = sizeof(rxBuffer);
if (ICOM_E_OK == icom_receive(&dev1ICOM, 0, rxBuffer, &length))
{
printf("[%d]: %s\r\n", length, rxBuffer);
}
if (count % 10 == 0)
{
icom_task(&dev1ICOM);
}
if (count % 1000 == 0)
{
}
}
static void test_base(void)
{
task_create(10, dev0_task);
// task_create(10, dev1_task);
}
/************************************************************************************/
/************************************* Command ************************************/
/************************************************************************************/
static void usage(void)
{
printf(
"Usage: icom [opt] [arg] ...\n"
"\n"
"options:\n"
" -e <execute> Specifies the function to execute, the default is the <base> test\n"
" <base> Test base function\n"
" <ut> Unit test\n"
" ...\n"
" -h Print help\n"
" -v Print version\n"
" -u [<period>] Unit test period, unit ms, the default is 1000ms\n"
"\n"
);
}
static int test(int argc, char *argv[])
{
char *execute = NULL;
int ut_period = 1000;
/* reset getopt */
command_opt_init();
while (1)
{
int opt = command_getopt(argc, argv, "e:hvu::");
if (opt == -1) break;
switch (opt)
{
// Add others opt here
case 'u' :
if (command_optarg) ut_period = atoi(command_optarg);
break;
case 'e' :
execute = command_optarg;
break;
case 'v' :
printf("icom version %d.%d.%d\r\n", ICOM_V_MAJOR, ICOM_V_MINOR, ICOM_V_PATCH);
return 0;
case '?':
printf("Unknown option `%c`\r\n", command_optopt);
return -1;
case 'h' :
default:
usage();
return 0;
}
}
if (execute)
{
if (!strcmp(execute, "base"))
{
test_base();
}
else if (!strcmp(execute, "ut"))
{
#if defined(TEST_TARGET_icom)
while (1)
{
unitt_task();
usleep(1000 * ut_period);
}
#else
printf("create task %d\r\n", task_create(ut_period, unitt_task));
#endif
}
}
else
{
test_base();
}
#if 0
if (1 == command_optind) // no opt
{
}
for (int index = command_optind; index < argc; index++)
{
if (!strcmp(argv[index], "base"))
{
test_base();
}
}
if (1 == argc)
{
test_base();
}
#endif
return 0;
}
/************************************************************************************/
/************************************ Test entry ************************************/
/************************************************************************************/
#if defined(TEST_TARGET_icom)
int main(int argc, char *argv[])
{
return test(argc, argv);
}
#else
void test_icom(void)
{
command_export("icom", test);
}
init_export_app(test_icom);
#endif