共2条
1/1 1 跳转至页
关于串口驱动及中间件的使用--影舞者
问
用了一段时间的2214,做了一个串口驱动和中间件的程序,此程序适用于
利用报文进行通信的地方,如电力设备等等,在应用中测试通信
环境支持:
1:UCOS-II操作系统
2:2214CPU
原先的通信问题:
1:在电力通信一般是以报文通信为主,串口每收到一个字节就中断一次,再由上层程序再处理,根据报文协议操作,这程序易乱,且维护不易,在扩展通信协议时更不好处理
2:CPU利用低
现在的通信修改,对通信仅提供五个函数,管理所有的通信口
ARM_UARTS_EXT uint8 Driver_Comm_Init(void);
ARM_UARTS_EXT uint8 Driver_Set_Comm_State(uint8 use_index,uint8 use_state);
ARM_UARTS_EXT uint8 Driver_Release_Comm(uint8 use_index);
ARM_UARTS_EXT uint8 Driver_Apply_Comm(uint8 use_for);
ARM_UARTS_EXT uint8 Driver_Comm_Frame(uint8 *Frame_Data,uint16 Frame_Length,uint8 uart_num,uint8 wait_echo);
此程序还没有处理超时接收问题,使用时注意
/*****************************************************************************************
版权所有: 影舞者
版本号: 2.52
文件名: ARM_UARTS.H
生成日期: 1900.1.1
作者: 影舞者
功能说明: 系统串口通信配置文件
其它说明: 管理ARM的所有串口通信,目前管理
UART0、UART1、SPI0、SPI1四个
*****************************************************************************************/
#ifdef ARM_UARTS_GLOBALS
#define ARM_UARTS_EXT
#else
#define ARM_UARTS_EXT extern
#endif
// 缓冲区设置
#define Frame_Number 60 // 帧数
#define Frame_Size 300 // 帧大小
ARM_UARTS_EXT uint8 Driver_Comm_Buf[Frame_Number][Frame_Size]; // 缓冲区
ARM_UARTS_EXT uint8 Driver_Comm_Users[Frame_Number]; // 帧用途
ARM_UARTS_EXT uint16 Driver_Comm_Number[Frame_Number]; // 帧里数据大小
ARM_UARTS_EXT uint16 Driver_Comm_Index[Frame_Number]; // 帧数据操作索引
ARM_UARTS_EXT OS_EVENT *DRIVER_COMM_OPERATE; // 缓冲区操作权信号量,有数据等待操作时,信号量不为0
// 通信监视开关,1-开.0-关
ARM_UARTS_EXT uint8 OPEN_UART0_COMM; // UART0通信监视开关
ARM_UARTS_EXT uint8 OPEN_UART1_COMM; // UART1通信监视开关
ARM_UARTS_EXT uint8 OPEN_SPI0_COMM; // SPI0通信监视开关
ARM_UARTS_EXT uint8 OPEN_SPI1_COMM; // SPI1通信监视开关
ARM_UARTS_EXT uint8 OPEN_POSSE_COMM; // 所有通信监视开关
// 是否应答
#define WAIT_ECHO 1
#define NO_ECHO 0
// UARTS报文缓冲区,帧用途:
#define COMM_BUF_IDLESSE 00 // 空闲空间
#define UART0_SEND 10 // 申请用于UART0发送数据
#define UART0_SEND_OK 11 // 处理完毕,数据可以UART0发送
#define UART0_RECEIVE 12 // 申请用于UART0发送数据
#define UART0_RECEIVE_OK 13 // 处理完毕,接收完UART0数据
#define UART1_SEND 20 // 申请用于UART1发送数据
#define UART1_SEND_OK 21 // 处理完毕,数据可以UART1发送
#define UART1_RECEIVE 22 // 申请用于UART1发送数据
#define UART1_RECEIVE_OK 23 // 处理完毕,接收完UART1数据
#define SPI0_SEND 30 // 申请用于SPI0发送数据
#define SPI0_SEND_OK 31 // 处理完毕,数据可以SPI0发送
#define SPI0_RECEIVE 32 // 申请用于SPI0发送数据
#define SPI0_RECEIVE_OK 33 // 处理完毕,接收完SPI0数据
#define SPI1_SEND 40 // 申请用于SPI1发送数据
#define SPI1_SEND_OK 41 // 处理完毕,数据可以SPI1发送
#define SPI1_RECEIVE 42 // 申请用于SPI1发送数据
#define SPI1_RECEIVE_OK 43 // 处理完毕,接收完SPI1数据
// UARTS
#define COMM_UART0 0 // UART0的标识
#define COMM_UART1 1 // UART1的标识
#define COMM_SPI0 2 // SPI0 的标识
#define COMM_SPI1 3 // SPI1 的标识
// UARTS 速度设置
#define DRIVE_UART0_BPS 115200 // UART0的波特率
#define DRIVE_UART1_BPS 4800 // UART1的波特率
#define DRIVE_SPI0_BPS 115200 // SPI0的波特率
#define DRIVE_SPI1_BPS 115200 // SPI1的波特率
// UART0的通信控制
#define UART0_RECEIVE_DELAY_TIME 3 // 超时时间长度(定时器1的倍数)
ARM_UARTS_EXT uint8 Uart0_Send_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Uart0_Send_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Uart0_Send_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 Uart0_Ask_Echo; // 要求响应数据
ARM_UARTS_EXT uint8 Uart0_Rece_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Uart0_Rece_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Uart0_Rece_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 UART0_RECEIVE_HEAD;
ARM_UARTS_EXT uint8 UART0_RECEIVE_DELAY;
ARM_UARTS_EXT uint8 UART0_IRQ_CHOICE; // 中断函数用到的变量
ARM_UARTS_EXT uint8 UART0_RBR_TEMP; // 申请为全局变量以加快中断程序
ARM_UARTS_EXT uint8 UART0_TEMP_RESULT;
ARM_UARTS_EXT OS_EVENT *UART0_SEND_USERING; // UART0总线发送操作权信号量
ARM_UARTS_EXT OS_EVENT *UART0_DRIVER_ECHO; // UART0应答信号量
// UART1的通信控制
#define UART1_RECEIVE_DELAY_TIME 3 // 超时时间长度(定时器1的倍数)
ARM_UARTS_EXT uint8 Uart1_Send_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Uart1_Send_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Uart1_Send_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 Uart1_Ask_Echo; // 要求响应数据
ARM_UARTS_EXT uint8 Uart1_Rece_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Uart1_Rece_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Uart1_Rece_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 UART1_RECEIVE_HEAD; // 报文头检测
ARM_UARTS_EXT uint8 UART1_RECEIVE_BEGIN; // 开始接收标志
ARM_UARTS_EXT uint8 UART1_RECEIVE_DELAY; // 接收延迟计数器
ARM_UARTS_EXT uint8 UART1_IRQ_CHOICE; // 中断函数变量
ARM_UARTS_EXT uint8 UART1_RBR_TEMP; // 中断函数变量,申请为全局变量以加快中断程序
ARM_UARTS_EXT uint8 UART1_TEMP_RESULT; // 中断函数变量
ARM_UARTS_EXT OS_EVENT *UART1_SEND_USERING; // UART1总线发送操作权信号量
ARM_UARTS_EXT OS_EVENT *UART1_DRIVER_ECHO; // UART1应答信号量
// SPI0的通信控制
#define SPI0_RECEIVE_DELAY_TIME 3 // 超时时间长度(定时器1的倍数)
ARM_UARTS_EXT uint8 Spi0_Send_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Spi0_Send_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Spi0_Send_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 Spi0_Ask_Echo; // 要求响应数据
ARM_UARTS_EXT uint8 Spi0_Rece_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Spi0_Rece_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Spi0_Rece_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 SPI0_RECEIVE_HEAD;
ARM_UARTS_EXT uint8 SPI0_RECEIVE_DELAY;
ARM_UARTS_EXT uint8 SPI0_IRQ_CHOICE; // 中断函数用到的变量
ARM_UARTS_EXT uint8 SPI0_RBR_TEMP; // 申请为全局变量以加快中断程序
ARM_UARTS_EXT uint8 SPI0_TEMP_RESULT;
ARM_UARTS_EXT OS_EVENT *SPI0_SEND_USERING; // SPI0总线发送操作权信号量
ARM_UARTS_EXT OS_EVENT *SPI0_DRIVER_ECHO; // SPI0应答信号量
// SPI1的通信控制
#define SPI1_RECEIVE_DELAY_TIME 3 // 超时时间长度(定时器1的倍数)
ARM_UARTS_EXT uint8 Spi1_Send_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Spi1_Send_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Spi1_Send_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 Spi1_Ask_Echo; // 要求响应数据
ARM_UARTS_EXT uint8 Spi1_Rece_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Spi1_Rece_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Spi1_Rece_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 SPI1_RECEIVE_HEAD;
ARM_UARTS_EXT uint8 SPI1_RECEIVE_DELAY;
ARM_UARTS_EXT uint8 SPI1_IRQ_CHOICE; // 中断函数用到的变量
ARM_UARTS_EXT uint8 SPI1_RBR_TEMP; // 申请为全局变量以加快中断程序
ARM_UARTS_EXT uint8 SPI1_TEMP_RESULT;
ARM_UARTS_EXT OS_EVENT *SPI1_SEND_USERING; // SPI1总线发送操作权信号量
ARM_UARTS_EXT OS_EVENT *SPI1_DRIVER_ECHO; // SPI1应答信号量
// 系统应用程序
ARM_UARTS_EXT uint8 Driver_Comm_Init(void);
ARM_UARTS_EXT uint8 Driver_Set_Comm_State(uint8 use_index,uint8 use_state);
ARM_UARTS_EXT uint8 Driver_Release_Comm(uint8 use_index);
ARM_UARTS_EXT uint8 Driver_Apply_Comm(uint8 use_for);
ARM_UARTS_EXT uint8 Driver_Comm_Frame(uint8 *Frame_Data,uint16 Frame_Length,uint8 uart_num,uint8 wait_echo);
/*****************************************************************************************
********************************END OF FILE*********************************************
*****************************************************************************************/
/*****************************************************************************************
版权所有: 影舞者
版本号: 2.52
文件名: ARM_UARTS.H
生成日期: 1900.1.1
作者: 影舞者
功能说明: 系统串口通信配置文件
其它说明: 管理ARM的所有串口通信,目前管理
UART0、UART1、SPI0、SPI1四个
*****************************************************************************************/
#define ARM_UARTS_GLOBALS // 定义变量类型
#include "includes.h" // 包含进UCOSII操作系统
#include "cfg_system.h" // 包含进用户程序其它文件
/*****************************************************************************************
函数名称: uint8 Driver_Comm_Init(void)
版本号: 2.52
生成日期: 1900.1.1
作者: 影舞者
功能说明: 串品通信初始化
输入参数:
输出参数: 0: 成功
1: 失败
其它说明: 一般在系统初始化时调用
*****************************************************************************************/
uint8 Driver_Comm_Init(void)
{
// 缓冲区信号量
DRIVER_COMM_OPERATE = OSSemCreate(0);
// 通信监视开关
OPEN_UART0_COMM = 0; // UART0通信监视开关
OPEN_UART1_COMM = 0; // UART1通信监视开关
OPEN_SPI0_COMM = 0; // SPI0通信监视开关
OPEN_SPI1_COMM = 0; // SPI1通信监视开关
OPEN_POSSE_COMM = 0;
// UART0的通信变量设置
Uart0_Send_Comm_Index = Frame_Number; // 指向空
Uart0_Send_Number = 0;
Uart0_Send_Index = 0;
Uart0_Ask_Echo = 0;
Uart0_Rece_Comm_Index = Frame_Number; // 指向空
Uart0_Rece_Number = 0;
Uart0_Rece_Index = 0;
UART0_RECEIVE_HEAD = 0;
UART0_RECEIVE_DELAY = 0;
UART0_SEND_USERING = OSSemCreate(1);
UART0_DRIVER_ECHO = OSSemCreate(0);
// UART1的通信变量设置
Uart1_Send_Comm_Index = Frame_Number; // 指向空
Uart1_Send_Number = 0;
Uart1_Send_Index = 0;
Uart1_Ask_Echo = 0;
Uart1_Rece_Comm_Index = Frame_Number; // 指向空
Uart1_Rece_Number = 0;
Uart1_Rece_Index = 0;
UART1_RECEIVE_HEAD = 0;
UART1_RECEIVE_BEGIN = 0;
UART1_RECEIVE_DELAY = 0;
UART1_SEND_USERING = OSSemCreate(1);
UART1_DRIVER_ECHO = OSSemCreate(0);
// SPI0的通信变量设置
Spi0_Send_Comm_Index = Frame_Number; // 指向空
Spi0_Send_Number = 0;
Spi0_Send_Index = 0;
Spi0_Ask_Echo = 0;
Spi0_Rece_Comm_Index = Frame_Number; // 指向空
Spi0_Rece_Number = 0;
Spi0_Rece_Index = 0;
SPI0_RECEIVE_HEAD = 0;
SPI0_RECEIVE_DELAY = 0;
SPI0_SEND_USERING = OSSemCreate(1);
SPI0_DRIVER_ECHO = OSSemCreate(0);
// SPI1的通信变量设置
Spi1_Send_Comm_Index = Frame_Number; // 指向空
Spi1_Send_Number = 0;
Spi1_Send_Index = 0;
Spi1_Ask_Echo = 0;
Spi1_Rece_Comm_Index = Frame_Number; // 指向空
Spi1_Rece_Number = 0;
Spi1_Rece_Index = 0;
SPI1_RECEIVE_HEAD = 0;
SPI1_RECEIVE_DELAY = 0;
SPI1_SEND_USERING = OSSemCreate(1);
SPI1_DRIVER_ECHO = OSSemCreate(0);
return 0; // 初始化成功
}
/*****************************************************************************************
函数名称: void Driver_Set_Comm_State(uint8 use_index,uint8 use_state)
版本号: 2.52
生成日期: 1900.1.1
作者: 影舞者
功能说明: 设置指定缓冲区的状态
输入参数: uint8 use_index: 缓冲区的索引
uint8 use_state: 指定的缓冲区的状态
输出参数: 0: 成功
1: 失败
其它说明: 无
*****************************************************************************************/
uint8 Driver_Set_Comm_State(uint8 use_index,uint8 use_state)
{
if(use_index >= Frame_Number)
return 1;
Driver_Comm_Users[use_index] = use_state;
return 0;
}
/*****************************************************************************************
函数名称: void Driver_Release_Comm(uint8 use_index)
版本号: 2.52
生成日期: 1900.1.1
作者: 影舞者
功能说明: 释放指定的缓冲区
输入参数: uint8 use_index: 指定的缓冲区
输出参数: 0: 成功
1: 失败
其它说明: 无
*****************************************************************************************/
uint8 Driver_Release_Comm(uint8 use_index)
{
if(use_index >= Frame_Number)
return 1;
Driver_Comm_Users[use_index] = COMM_BUF_IDLESSE; // 帧用途
Driver_Comm_Number[use_index] = 0; // 帧里数据大小
Driver_Comm_Index[use_index] = 0;
return 0;
}
/*****************************************************************************************
函数名称: uint8 Driver_Apply_Comm(uint8 use_for)
版本号: 2.52
生成日期: 1900.1.1
作者: 影舞者
功能说明: 申请一个缓冲区
输入参数: uint8 use_for: 缓冲区的用途
输出参数: Frame_Number: 没有空闲
0--(Frame_Number-1): 空闲空间
其它说明: 无
*****************************************************************************************/
uint8 Driver_Apply_Comm(uint8 use_for)
{
uint8 i;
OS_ENTER_CRITICAL();
for(i=0;i<Frame_Number;i++)
{
if(Driver_Comm_Users[i] == 0)
{
Driver_Comm_Users[i] = use_for;
OS_EXIT_CRITICAL();
return i;
}
}
OS_EXIT_CRITICAL();
return Frame_Number;
}
/*****************************************************************************************
函数名称: uint8 Driver_Comm_Frame(uint8 *Frame_Data,uint16 Frame_Length,uint8 uart_num)
版本号: 2.52
生成日期: 1900.1.1
作者: 影舞者
功能说明: UART发送一帧数据
输入参数: uint8 *Frame_Data: 要发送的数据
uint16 Frame_Length: 数据长度,字节数
uint8 uart_num: 哪个串口 0-UART0,1-UART1,2-SPI0,3-SPI1
uint8 wait_echo: 0-不等待应答,1-不等待应答
输出参数: 0: 成功
1: 失败
其它说明: 本函数完成打包任务,报文头+长度+(数据)+校验码
*****************************************************************************************/
uint8 Driver_Comm_Frame(uint8 *Frame_Data,uint16 Frame_Length,uint8 uart_num,uint8 wait_echo)
{
uint8 buf_index;
uint16 i,accdata;
if(Frame_Length < 1) // 没有数据
return 1;
if((wait_echo != WAIT_ECHO)&&(wait_echo != NO_ECHO)) // 参数错误
return 1;
if(uart_num == COMM_UART0) // UART0
{
// UART0
return 1;
}
else if(uart_num == COMM_UART1) // UART1
{
buf_index = Driver_Apply_Comm(UART1_SEND); // 申请一个空间
if(buf_index == Frame_Number) // 申请不到空间
return 1;
Driver_Comm_Buf[buf_index][0] = 0xA5; // 填充报文头
Driver_Comm_Buf[buf_index][1] = 0x5A;
Driver_Comm_Buf[buf_index][2] = 0xA5;
Driver_Comm_Buf[buf_index][3] = 0x5A;
Frame_Length = Frame_Length+8; // 报文总长度
Driver_Comm_Buf[buf_index][4] = Frame_Length&0xFF; // 报文长度,低位在前,高位在后
Driver_Comm_Buf[buf_index][5] = (Frame_Length>>8)&0xFF;
Frame_Length = Frame_Length-8; // 报文数据
for(i=0;i<Frame_Length;i++)
{
Driver_Comm_Buf[buf_index][6+i] = Frame_Data[i];
}
Frame_Length = Frame_Length+6; // 报文校验码
accdata = 0;
for(i=0;i<Frame_Length;i++)
{
accdata=accdata+Driver_Comm_Buf[buf_index][i];
}
Driver_Comm_Buf[buf_index][i] = accdata&0xFF; // 低位在前
Driver_Comm_Buf[buf_index][i+1] = (accdata>>8)&0xFF; // 高位在后
// 设置帧状态
if(wait_echo == WAIT_ECHO)
Uart1_Ask_Echo = WAIT_ECHO;
else
Uart1_Ask_Echo = NO_ECHO;
Driver_Comm_Number[buf_index] = Frame_Length+2; // 帧里数据大小
Driver_Comm_Index[buf_index] = 0; // 帧数据操作索引
Driver_Set_Comm_State(buf_index,UART1_SEND_OK); // 可以发送数据
OSSemPost(DRIVER_COMM_OPERATE); // 发出信号量,启动发送任务
return 0;
}
else if(uart_num == COMM_SPI0) // SPI0
{
return 1;
}
else if(uart_num == COMM_SPI1) // SPI1
{
return 1;
}
return 1;
}
/*****************************************************************************************
********************************END OF FILE*********************************************
*****************************************************************************************/
应用法
1:在主程序在建立一个任务,专门用来处理通信口收到的数据
/*****************************************************************************************
函数名称: void Task_Driver_Comm_Operate(void *pdata)
版本号: 2.52
生成日期: 1900.1.1
作者: 影舞者
功能说明: 串口是间件的管理任务
输入参数:
输出参数:
其它说明: 所有的串口收发操作全在这里进行
*****************************************************************************************/
void Task_Driver_Comm_Operate(void *pdata)
{
uint8 ii,result;
while (1)
{
OSSemPend(DRIVER_COMM_OPERATE,0,&result); // 无限期进入等待信号
if(result == OS_NO_ERR) // 缓冲区有收、发数据信号
{
for(ii=0;ii<Frame_Number;ii++) // 查询缓冲区的用途
{
switch(Driver_Comm_Users[ii]) // 分类处理缓冲区数据
{
case UART0_SEND_OK: // 有UART0发送的数据
break;
case UART0_RECEIVE_OK: // 有UART0接收的数据
break;
case UART1_SEND_OK: // 有UART1发送的数据
result = OSSemAccept(UART1_SEND_USERING); // 申请操作权
if(result != 0) // 取得操作权
{
if((OPEN_UART1_COMM == 1)||(OPEN_POSSE_COMM == 1)) // 显示发送数据
{
Disp_String("uart1 send: ");
Disp_Pack(Driver_Comm_Buf[ii],Driver_Comm_Number[ii]);
Disp_String("\n");
}
Uart1_Send_Comm_Index = ii; // 指向缓冲区
Uart1_Send_Number = Driver_Comm_Number[ii]; // 发送个数
Uart1_Send_Index = Driver_Comm_Index[ii]; // 发送下索引号
U1THR = Driver_Comm_Buf[Uart1_Send_Comm_Index][0]; // 发送第一个字节
Uart1_Send_Index = 1; // 索引值指向第二个字节
}
break;
case UART1_RECEIVE_OK: // 有UART1接收的数据
if((OPEN_UART1_COMM == 1)||(OPEN_POSSE_COMM == 1)) // 显示发送数据
{
Disp_String("uart1 rece: ");
Disp_Pack(Driver_Comm_Buf[ii],Driver_Comm_Number[ii]);
Disp_String("\n");
}
// ***************************
// ***************************
Driver_Release_Comm(ii); // 释放空间
break;
case SPI0_SEND_OK: // 有SPI0发送的数据
break;
case SPI0_RECEIVE_OK: // 有SPI0接收的数据
break;
case SPI1_SEND_OK: // 有SPI1发送的数据
break;
case SPI1_RECEIVE_OK: // 有SPI1接收的数据
break;
}// 数据分别处理结束
}// FOR结束
}
// 信号返回结束
}
// 主程序结束
}
2:发送使用
Driver_Comm_Frame(data_buf,5,COMM_UART1,NO_ECHO);
3:驱动,仅一个
/*****************************************************************************************
函数名称: void ANSI_IRQ_UART1_SERVERS(void)
版本号: 2.52
生成日期: 1900.1.1
作者: 影舞者
功能说明: 串口1中断服务程序,接收中断,发送中断
输入参数:
输出参数:
其它说明: 由汇编中断调用
*****************************************************************************************/
void ANSI_IRQ_UART1_SERVERS(void)
{
UART1_IRQ_CHOICE = U1IIR; // 读状态,同时清中断
VICVectAddr = 0x00; // 通知VIC中断处理结束
if(UART1_IRQ_CHOICE & 0x02) // 发送中断
{
if(Uart1_Send_Index < Uart1_Send_Number) // 发送数据
{
利用报文进行通信的地方,如电力设备等等,在应用中测试通信
环境支持:
1:UCOS-II操作系统
2:2214CPU
原先的通信问题:
1:在电力通信一般是以报文通信为主,串口每收到一个字节就中断一次,再由上层程序再处理,根据报文协议操作,这程序易乱,且维护不易,在扩展通信协议时更不好处理
2:CPU利用低
现在的通信修改,对通信仅提供五个函数,管理所有的通信口
ARM_UARTS_EXT uint8 Driver_Comm_Init(void);
ARM_UARTS_EXT uint8 Driver_Set_Comm_State(uint8 use_index,uint8 use_state);
ARM_UARTS_EXT uint8 Driver_Release_Comm(uint8 use_index);
ARM_UARTS_EXT uint8 Driver_Apply_Comm(uint8 use_for);
ARM_UARTS_EXT uint8 Driver_Comm_Frame(uint8 *Frame_Data,uint16 Frame_Length,uint8 uart_num,uint8 wait_echo);
此程序还没有处理超时接收问题,使用时注意
/*****************************************************************************************
版权所有: 影舞者
版本号: 2.52
文件名: ARM_UARTS.H
生成日期: 1900.1.1
作者: 影舞者
功能说明: 系统串口通信配置文件
其它说明: 管理ARM的所有串口通信,目前管理
UART0、UART1、SPI0、SPI1四个
*****************************************************************************************/
#ifdef ARM_UARTS_GLOBALS
#define ARM_UARTS_EXT
#else
#define ARM_UARTS_EXT extern
#endif
// 缓冲区设置
#define Frame_Number 60 // 帧数
#define Frame_Size 300 // 帧大小
ARM_UARTS_EXT uint8 Driver_Comm_Buf[Frame_Number][Frame_Size]; // 缓冲区
ARM_UARTS_EXT uint8 Driver_Comm_Users[Frame_Number]; // 帧用途
ARM_UARTS_EXT uint16 Driver_Comm_Number[Frame_Number]; // 帧里数据大小
ARM_UARTS_EXT uint16 Driver_Comm_Index[Frame_Number]; // 帧数据操作索引
ARM_UARTS_EXT OS_EVENT *DRIVER_COMM_OPERATE; // 缓冲区操作权信号量,有数据等待操作时,信号量不为0
// 通信监视开关,1-开.0-关
ARM_UARTS_EXT uint8 OPEN_UART0_COMM; // UART0通信监视开关
ARM_UARTS_EXT uint8 OPEN_UART1_COMM; // UART1通信监视开关
ARM_UARTS_EXT uint8 OPEN_SPI0_COMM; // SPI0通信监视开关
ARM_UARTS_EXT uint8 OPEN_SPI1_COMM; // SPI1通信监视开关
ARM_UARTS_EXT uint8 OPEN_POSSE_COMM; // 所有通信监视开关
// 是否应答
#define WAIT_ECHO 1
#define NO_ECHO 0
// UARTS报文缓冲区,帧用途:
#define COMM_BUF_IDLESSE 00 // 空闲空间
#define UART0_SEND 10 // 申请用于UART0发送数据
#define UART0_SEND_OK 11 // 处理完毕,数据可以UART0发送
#define UART0_RECEIVE 12 // 申请用于UART0发送数据
#define UART0_RECEIVE_OK 13 // 处理完毕,接收完UART0数据
#define UART1_SEND 20 // 申请用于UART1发送数据
#define UART1_SEND_OK 21 // 处理完毕,数据可以UART1发送
#define UART1_RECEIVE 22 // 申请用于UART1发送数据
#define UART1_RECEIVE_OK 23 // 处理完毕,接收完UART1数据
#define SPI0_SEND 30 // 申请用于SPI0发送数据
#define SPI0_SEND_OK 31 // 处理完毕,数据可以SPI0发送
#define SPI0_RECEIVE 32 // 申请用于SPI0发送数据
#define SPI0_RECEIVE_OK 33 // 处理完毕,接收完SPI0数据
#define SPI1_SEND 40 // 申请用于SPI1发送数据
#define SPI1_SEND_OK 41 // 处理完毕,数据可以SPI1发送
#define SPI1_RECEIVE 42 // 申请用于SPI1发送数据
#define SPI1_RECEIVE_OK 43 // 处理完毕,接收完SPI1数据
// UARTS
#define COMM_UART0 0 // UART0的标识
#define COMM_UART1 1 // UART1的标识
#define COMM_SPI0 2 // SPI0 的标识
#define COMM_SPI1 3 // SPI1 的标识
// UARTS 速度设置
#define DRIVE_UART0_BPS 115200 // UART0的波特率
#define DRIVE_UART1_BPS 4800 // UART1的波特率
#define DRIVE_SPI0_BPS 115200 // SPI0的波特率
#define DRIVE_SPI1_BPS 115200 // SPI1的波特率
// UART0的通信控制
#define UART0_RECEIVE_DELAY_TIME 3 // 超时时间长度(定时器1的倍数)
ARM_UARTS_EXT uint8 Uart0_Send_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Uart0_Send_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Uart0_Send_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 Uart0_Ask_Echo; // 要求响应数据
ARM_UARTS_EXT uint8 Uart0_Rece_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Uart0_Rece_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Uart0_Rece_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 UART0_RECEIVE_HEAD;
ARM_UARTS_EXT uint8 UART0_RECEIVE_DELAY;
ARM_UARTS_EXT uint8 UART0_IRQ_CHOICE; // 中断函数用到的变量
ARM_UARTS_EXT uint8 UART0_RBR_TEMP; // 申请为全局变量以加快中断程序
ARM_UARTS_EXT uint8 UART0_TEMP_RESULT;
ARM_UARTS_EXT OS_EVENT *UART0_SEND_USERING; // UART0总线发送操作权信号量
ARM_UARTS_EXT OS_EVENT *UART0_DRIVER_ECHO; // UART0应答信号量
// UART1的通信控制
#define UART1_RECEIVE_DELAY_TIME 3 // 超时时间长度(定时器1的倍数)
ARM_UARTS_EXT uint8 Uart1_Send_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Uart1_Send_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Uart1_Send_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 Uart1_Ask_Echo; // 要求响应数据
ARM_UARTS_EXT uint8 Uart1_Rece_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Uart1_Rece_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Uart1_Rece_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 UART1_RECEIVE_HEAD; // 报文头检测
ARM_UARTS_EXT uint8 UART1_RECEIVE_BEGIN; // 开始接收标志
ARM_UARTS_EXT uint8 UART1_RECEIVE_DELAY; // 接收延迟计数器
ARM_UARTS_EXT uint8 UART1_IRQ_CHOICE; // 中断函数变量
ARM_UARTS_EXT uint8 UART1_RBR_TEMP; // 中断函数变量,申请为全局变量以加快中断程序
ARM_UARTS_EXT uint8 UART1_TEMP_RESULT; // 中断函数变量
ARM_UARTS_EXT OS_EVENT *UART1_SEND_USERING; // UART1总线发送操作权信号量
ARM_UARTS_EXT OS_EVENT *UART1_DRIVER_ECHO; // UART1应答信号量
// SPI0的通信控制
#define SPI0_RECEIVE_DELAY_TIME 3 // 超时时间长度(定时器1的倍数)
ARM_UARTS_EXT uint8 Spi0_Send_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Spi0_Send_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Spi0_Send_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 Spi0_Ask_Echo; // 要求响应数据
ARM_UARTS_EXT uint8 Spi0_Rece_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Spi0_Rece_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Spi0_Rece_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 SPI0_RECEIVE_HEAD;
ARM_UARTS_EXT uint8 SPI0_RECEIVE_DELAY;
ARM_UARTS_EXT uint8 SPI0_IRQ_CHOICE; // 中断函数用到的变量
ARM_UARTS_EXT uint8 SPI0_RBR_TEMP; // 申请为全局变量以加快中断程序
ARM_UARTS_EXT uint8 SPI0_TEMP_RESULT;
ARM_UARTS_EXT OS_EVENT *SPI0_SEND_USERING; // SPI0总线发送操作权信号量
ARM_UARTS_EXT OS_EVENT *SPI0_DRIVER_ECHO; // SPI0应答信号量
// SPI1的通信控制
#define SPI1_RECEIVE_DELAY_TIME 3 // 超时时间长度(定时器1的倍数)
ARM_UARTS_EXT uint8 Spi1_Send_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Spi1_Send_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Spi1_Send_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 Spi1_Ask_Echo; // 要求响应数据
ARM_UARTS_EXT uint8 Spi1_Rece_Comm_Index; // 缓冲区的位置
ARM_UARTS_EXT uint16 Spi1_Rece_Number; // 帧里数据大小
ARM_UARTS_EXT uint16 Spi1_Rece_Index; // 帧数据操作索引
ARM_UARTS_EXT uint8 SPI1_RECEIVE_HEAD;
ARM_UARTS_EXT uint8 SPI1_RECEIVE_DELAY;
ARM_UARTS_EXT uint8 SPI1_IRQ_CHOICE; // 中断函数用到的变量
ARM_UARTS_EXT uint8 SPI1_RBR_TEMP; // 申请为全局变量以加快中断程序
ARM_UARTS_EXT uint8 SPI1_TEMP_RESULT;
ARM_UARTS_EXT OS_EVENT *SPI1_SEND_USERING; // SPI1总线发送操作权信号量
ARM_UARTS_EXT OS_EVENT *SPI1_DRIVER_ECHO; // SPI1应答信号量
// 系统应用程序
ARM_UARTS_EXT uint8 Driver_Comm_Init(void);
ARM_UARTS_EXT uint8 Driver_Set_Comm_State(uint8 use_index,uint8 use_state);
ARM_UARTS_EXT uint8 Driver_Release_Comm(uint8 use_index);
ARM_UARTS_EXT uint8 Driver_Apply_Comm(uint8 use_for);
ARM_UARTS_EXT uint8 Driver_Comm_Frame(uint8 *Frame_Data,uint16 Frame_Length,uint8 uart_num,uint8 wait_echo);
/*****************************************************************************************
********************************END OF FILE*********************************************
*****************************************************************************************/
/*****************************************************************************************
版权所有: 影舞者
版本号: 2.52
文件名: ARM_UARTS.H
生成日期: 1900.1.1
作者: 影舞者
功能说明: 系统串口通信配置文件
其它说明: 管理ARM的所有串口通信,目前管理
UART0、UART1、SPI0、SPI1四个
*****************************************************************************************/
#define ARM_UARTS_GLOBALS // 定义变量类型
#include "includes.h" // 包含进UCOSII操作系统
#include "cfg_system.h" // 包含进用户程序其它文件
/*****************************************************************************************
函数名称: uint8 Driver_Comm_Init(void)
版本号: 2.52
生成日期: 1900.1.1
作者: 影舞者
功能说明: 串品通信初始化
输入参数:
输出参数: 0: 成功
1: 失败
其它说明: 一般在系统初始化时调用
*****************************************************************************************/
uint8 Driver_Comm_Init(void)
{
// 缓冲区信号量
DRIVER_COMM_OPERATE = OSSemCreate(0);
// 通信监视开关
OPEN_UART0_COMM = 0; // UART0通信监视开关
OPEN_UART1_COMM = 0; // UART1通信监视开关
OPEN_SPI0_COMM = 0; // SPI0通信监视开关
OPEN_SPI1_COMM = 0; // SPI1通信监视开关
OPEN_POSSE_COMM = 0;
// UART0的通信变量设置
Uart0_Send_Comm_Index = Frame_Number; // 指向空
Uart0_Send_Number = 0;
Uart0_Send_Index = 0;
Uart0_Ask_Echo = 0;
Uart0_Rece_Comm_Index = Frame_Number; // 指向空
Uart0_Rece_Number = 0;
Uart0_Rece_Index = 0;
UART0_RECEIVE_HEAD = 0;
UART0_RECEIVE_DELAY = 0;
UART0_SEND_USERING = OSSemCreate(1);
UART0_DRIVER_ECHO = OSSemCreate(0);
// UART1的通信变量设置
Uart1_Send_Comm_Index = Frame_Number; // 指向空
Uart1_Send_Number = 0;
Uart1_Send_Index = 0;
Uart1_Ask_Echo = 0;
Uart1_Rece_Comm_Index = Frame_Number; // 指向空
Uart1_Rece_Number = 0;
Uart1_Rece_Index = 0;
UART1_RECEIVE_HEAD = 0;
UART1_RECEIVE_BEGIN = 0;
UART1_RECEIVE_DELAY = 0;
UART1_SEND_USERING = OSSemCreate(1);
UART1_DRIVER_ECHO = OSSemCreate(0);
// SPI0的通信变量设置
Spi0_Send_Comm_Index = Frame_Number; // 指向空
Spi0_Send_Number = 0;
Spi0_Send_Index = 0;
Spi0_Ask_Echo = 0;
Spi0_Rece_Comm_Index = Frame_Number; // 指向空
Spi0_Rece_Number = 0;
Spi0_Rece_Index = 0;
SPI0_RECEIVE_HEAD = 0;
SPI0_RECEIVE_DELAY = 0;
SPI0_SEND_USERING = OSSemCreate(1);
SPI0_DRIVER_ECHO = OSSemCreate(0);
// SPI1的通信变量设置
Spi1_Send_Comm_Index = Frame_Number; // 指向空
Spi1_Send_Number = 0;
Spi1_Send_Index = 0;
Spi1_Ask_Echo = 0;
Spi1_Rece_Comm_Index = Frame_Number; // 指向空
Spi1_Rece_Number = 0;
Spi1_Rece_Index = 0;
SPI1_RECEIVE_HEAD = 0;
SPI1_RECEIVE_DELAY = 0;
SPI1_SEND_USERING = OSSemCreate(1);
SPI1_DRIVER_ECHO = OSSemCreate(0);
return 0; // 初始化成功
}
/*****************************************************************************************
函数名称: void Driver_Set_Comm_State(uint8 use_index,uint8 use_state)
版本号: 2.52
生成日期: 1900.1.1
作者: 影舞者
功能说明: 设置指定缓冲区的状态
输入参数: uint8 use_index: 缓冲区的索引
uint8 use_state: 指定的缓冲区的状态
输出参数: 0: 成功
1: 失败
其它说明: 无
*****************************************************************************************/
uint8 Driver_Set_Comm_State(uint8 use_index,uint8 use_state)
{
if(use_index >= Frame_Number)
return 1;
Driver_Comm_Users[use_index] = use_state;
return 0;
}
/*****************************************************************************************
函数名称: void Driver_Release_Comm(uint8 use_index)
版本号: 2.52
生成日期: 1900.1.1
作者: 影舞者
功能说明: 释放指定的缓冲区
输入参数: uint8 use_index: 指定的缓冲区
输出参数: 0: 成功
1: 失败
其它说明: 无
*****************************************************************************************/
uint8 Driver_Release_Comm(uint8 use_index)
{
if(use_index >= Frame_Number)
return 1;
Driver_Comm_Users[use_index] = COMM_BUF_IDLESSE; // 帧用途
Driver_Comm_Number[use_index] = 0; // 帧里数据大小
Driver_Comm_Index[use_index] = 0;
return 0;
}
/*****************************************************************************************
函数名称: uint8 Driver_Apply_Comm(uint8 use_for)
版本号: 2.52
生成日期: 1900.1.1
作者: 影舞者
功能说明: 申请一个缓冲区
输入参数: uint8 use_for: 缓冲区的用途
输出参数: Frame_Number: 没有空闲
0--(Frame_Number-1): 空闲空间
其它说明: 无
*****************************************************************************************/
uint8 Driver_Apply_Comm(uint8 use_for)
{
uint8 i;
OS_ENTER_CRITICAL();
for(i=0;i<Frame_Number;i++)
{
if(Driver_Comm_Users[i] == 0)
{
Driver_Comm_Users[i] = use_for;
OS_EXIT_CRITICAL();
return i;
}
}
OS_EXIT_CRITICAL();
return Frame_Number;
}
/*****************************************************************************************
函数名称: uint8 Driver_Comm_Frame(uint8 *Frame_Data,uint16 Frame_Length,uint8 uart_num)
版本号: 2.52
生成日期: 1900.1.1
作者: 影舞者
功能说明: UART发送一帧数据
输入参数: uint8 *Frame_Data: 要发送的数据
uint16 Frame_Length: 数据长度,字节数
uint8 uart_num: 哪个串口 0-UART0,1-UART1,2-SPI0,3-SPI1
uint8 wait_echo: 0-不等待应答,1-不等待应答
输出参数: 0: 成功
1: 失败
其它说明: 本函数完成打包任务,报文头+长度+(数据)+校验码
*****************************************************************************************/
uint8 Driver_Comm_Frame(uint8 *Frame_Data,uint16 Frame_Length,uint8 uart_num,uint8 wait_echo)
{
uint8 buf_index;
uint16 i,accdata;
if(Frame_Length < 1) // 没有数据
return 1;
if((wait_echo != WAIT_ECHO)&&(wait_echo != NO_ECHO)) // 参数错误
return 1;
if(uart_num == COMM_UART0) // UART0
{
// UART0
return 1;
}
else if(uart_num == COMM_UART1) // UART1
{
buf_index = Driver_Apply_Comm(UART1_SEND); // 申请一个空间
if(buf_index == Frame_Number) // 申请不到空间
return 1;
Driver_Comm_Buf[buf_index][0] = 0xA5; // 填充报文头
Driver_Comm_Buf[buf_index][1] = 0x5A;
Driver_Comm_Buf[buf_index][2] = 0xA5;
Driver_Comm_Buf[buf_index][3] = 0x5A;
Frame_Length = Frame_Length+8; // 报文总长度
Driver_Comm_Buf[buf_index][4] = Frame_Length&0xFF; // 报文长度,低位在前,高位在后
Driver_Comm_Buf[buf_index][5] = (Frame_Length>>8)&0xFF;
Frame_Length = Frame_Length-8; // 报文数据
for(i=0;i<Frame_Length;i++)
{
Driver_Comm_Buf[buf_index][6+i] = Frame_Data[i];
}
Frame_Length = Frame_Length+6; // 报文校验码
accdata = 0;
for(i=0;i<Frame_Length;i++)
{
accdata=accdata+Driver_Comm_Buf[buf_index][i];
}
Driver_Comm_Buf[buf_index][i] = accdata&0xFF; // 低位在前
Driver_Comm_Buf[buf_index][i+1] = (accdata>>8)&0xFF; // 高位在后
// 设置帧状态
if(wait_echo == WAIT_ECHO)
Uart1_Ask_Echo = WAIT_ECHO;
else
Uart1_Ask_Echo = NO_ECHO;
Driver_Comm_Number[buf_index] = Frame_Length+2; // 帧里数据大小
Driver_Comm_Index[buf_index] = 0; // 帧数据操作索引
Driver_Set_Comm_State(buf_index,UART1_SEND_OK); // 可以发送数据
OSSemPost(DRIVER_COMM_OPERATE); // 发出信号量,启动发送任务
return 0;
}
else if(uart_num == COMM_SPI0) // SPI0
{
return 1;
}
else if(uart_num == COMM_SPI1) // SPI1
{
return 1;
}
return 1;
}
/*****************************************************************************************
********************************END OF FILE*********************************************
*****************************************************************************************/
应用法
1:在主程序在建立一个任务,专门用来处理通信口收到的数据
/*****************************************************************************************
函数名称: void Task_Driver_Comm_Operate(void *pdata)
版本号: 2.52
生成日期: 1900.1.1
作者: 影舞者
功能说明: 串口是间件的管理任务
输入参数:
输出参数:
其它说明: 所有的串口收发操作全在这里进行
*****************************************************************************************/
void Task_Driver_Comm_Operate(void *pdata)
{
uint8 ii,result;
while (1)
{
OSSemPend(DRIVER_COMM_OPERATE,0,&result); // 无限期进入等待信号
if(result == OS_NO_ERR) // 缓冲区有收、发数据信号
{
for(ii=0;ii<Frame_Number;ii++) // 查询缓冲区的用途
{
switch(Driver_Comm_Users[ii]) // 分类处理缓冲区数据
{
case UART0_SEND_OK: // 有UART0发送的数据
break;
case UART0_RECEIVE_OK: // 有UART0接收的数据
break;
case UART1_SEND_OK: // 有UART1发送的数据
result = OSSemAccept(UART1_SEND_USERING); // 申请操作权
if(result != 0) // 取得操作权
{
if((OPEN_UART1_COMM == 1)||(OPEN_POSSE_COMM == 1)) // 显示发送数据
{
Disp_String("uart1 send: ");
Disp_Pack(Driver_Comm_Buf[ii],Driver_Comm_Number[ii]);
Disp_String("\n");
}
Uart1_Send_Comm_Index = ii; // 指向缓冲区
Uart1_Send_Number = Driver_Comm_Number[ii]; // 发送个数
Uart1_Send_Index = Driver_Comm_Index[ii]; // 发送下索引号
U1THR = Driver_Comm_Buf[Uart1_Send_Comm_Index][0]; // 发送第一个字节
Uart1_Send_Index = 1; // 索引值指向第二个字节
}
break;
case UART1_RECEIVE_OK: // 有UART1接收的数据
if((OPEN_UART1_COMM == 1)||(OPEN_POSSE_COMM == 1)) // 显示发送数据
{
Disp_String("uart1 rece: ");
Disp_Pack(Driver_Comm_Buf[ii],Driver_Comm_Number[ii]);
Disp_String("\n");
}
// ***************************
// ***************************
Driver_Release_Comm(ii); // 释放空间
break;
case SPI0_SEND_OK: // 有SPI0发送的数据
break;
case SPI0_RECEIVE_OK: // 有SPI0接收的数据
break;
case SPI1_SEND_OK: // 有SPI1发送的数据
break;
case SPI1_RECEIVE_OK: // 有SPI1接收的数据
break;
}// 数据分别处理结束
}// FOR结束
}
// 信号返回结束
}
// 主程序结束
}
2:发送使用
Driver_Comm_Frame(data_buf,5,COMM_UART1,NO_ECHO);
3:驱动,仅一个
/*****************************************************************************************
函数名称: void ANSI_IRQ_UART1_SERVERS(void)
版本号: 2.52
生成日期: 1900.1.1
作者: 影舞者
功能说明: 串口1中断服务程序,接收中断,发送中断
输入参数:
输出参数:
其它说明: 由汇编中断调用
*****************************************************************************************/
void ANSI_IRQ_UART1_SERVERS(void)
{
UART1_IRQ_CHOICE = U1IIR; // 读状态,同时清中断
VICVectAddr = 0x00; // 通知VIC中断处理结束
if(UART1_IRQ_CHOICE & 0x02) // 发送中断
{
if(Uart1_Send_Index < Uart1_Send_Number) // 发送数据
{
共2条
1/1 1 跳转至页
回复
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |
打赏帖 | |
---|---|
vscode+cmake搭建雅特力AT32L021开发环境被打赏30分 | |
【换取逻辑分析仪】自制底板并驱动ArduinoNanoRP2040ConnectLCD扩展板被打赏47分 | |
【分享评测,赢取加热台】RISC-V GCC 内嵌汇编使用被打赏38分 | |
【换取逻辑分析仪】-基于ADI单片机MAX78000的简易MP3音乐播放器被打赏48分 | |
我想要一部加热台+树莓派PICO驱动AHT10被打赏38分 | |
【换取逻辑分析仪】-硬件SPI驱动OLED屏幕被打赏36分 | |
换逻辑分析仪+上下拉与多路选择器被打赏29分 | |
Let'sdo第3期任务合集被打赏50分 | |
换逻辑分析仪+Verilog三态门被打赏27分 | |
换逻辑分析仪+Verilog多输出门被打赏24分 |