co_routine.h分析

本文档详细介绍了co_routine.h文件中定义的协程管理结构体和函数,包括协程的创建、运行、挂起、释放等操作,以及共享栈、事件处理、钩子系统调用等功能。通过解析这些API,开发者可以深入了解libco库如何实现高效的协程调度。

co_routine.h


#ifndef __CO_ROUTINE_H__
#define __CO_ROUTINE_H__

#include <stdint.h>
#include <sys/poll.h>
#include <pthread.h>

//1.struct 

struct stCoRoutine_t;//协程的结构体 用来存储协程相关的信息 具体含义会在 co_routine.cpp 给出 
struct stShareStack_t;//共享栈结构体 存储共享栈相关信息 具体信息会在 co_routine_inner.h 给出
struct stCoRoutineAttr_t // 用来初始化共享栈时使用的结构体。
{
	int stack_size; //栈的大小
	stShareStack_t*  share_stack; //分配的共享栈
	stCoRoutineAttr_t()//默认构造函数
	{
		stack_size = 128 * 1024;
		share_stack = NULL;
	}
}__attribute__ ((packed));
		/*
			__attribute__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节		
			数进行对齐,是GCC特有的语法。(http://blog.chinaunix.net/uid-25768133-id-3485479.html)
		*/
struct stCoEpoll_t;//控制定时的结构体 用来管理时间请求
typedef int (*pfn_co_eventloop_t)(void *); //定义一个函数指针 参数类型为一个 void *  返回值类型为int
typedef void *(*pfn_co_routine_t)( void * );// 基本同上

//2.co_routine 

int 	co_create( stCoRoutine_t **co,const stCoRoutineAttr_t *attr,void *(*routine)(void*),void *arg );
		/*	
			线程create函数,用来创建创建一个线程,注意和线程不同,create只会创建协程,不会去运行协程。
			stCoRoutine_t **co 当前协程 调用函数之后会对co进行赋值。
			const stCoRoutineAttr_t *attr 使用哪个栈进行分配,以及分配的大小。传参不为NULL的话,cIsShareStack会赋值true。
			void *(*routine)(void*) 协程调用的函数
			void *arg 协程调用的函数 使用的参数 类似于线程的用法。
		*/
void    co_resume( stCoRoutine_t *co );//运行当前的协程,协程加入调用栈中,然后调用co_swap,交换,使进程/线程 运行当前协程
void    co_yield( stCoRoutine_t *co );//作用同下
void    co_yield_ct(); 
		/*ct = current thread  
			让出协程,调用上一个入栈的协程。除非加入事件到时间轮中,不然当前协程不会再次运行。			
			底层调用co_yield_env。 libco协程是按照调用栈的顺序运行的,
			因此传参的co其实是没用的x co_yield co_yield_ct co_yield_env 作用一样。
		*/
void    co_release( stCoRoutine_t *co );//调用co_free 释放内存

stCoRoutine_t *co_self(); //返回当前协程 其实就是调用栈栈顶的协程。

int		co_poll( stCoEpoll_t *ctx,struct pollfd fds[], nfds_t nfds, int timeout_ms );
		/*
			调用了co_poll_inner 
			主要的作用为将需要监听的事件加入时间轮中,并让出协程。以及在事件触发后恢复函数执行,清理添加的事件。
			stCoEpoll_t *ctx 当前协成,会使用协成中的参数
			struct pollfd fds[] 时间数组
			nfds_t nfds 数组大小
			int timeout_ms 定时时间 
		*/
void 	co_eventloop( stCoEpoll_t *ctx,pfn_co_eventloop_t pfn,void *arg );
		/*
			主要功能为对时间进行轮询,不断调用epoll,超时事件则运行对应的协程。
			stCoEpoll_t *ctx 存储定时事件
			pfn_co_eventloop_t pfn,void *arg
			if( pfn )
			{
				if( -1 == pfn( arg ) )
				{
					break;
				}
			}
			函数中使用这个两个参数用来检测是否推出函数,通常为NULL,NULL。
		*/

//3.specific

int 	co_setspecific( pthread_key_t key, const void *value );//设置key对应的value。
void *	co_getspecific( pthread_key_t key );//获取key对应的value

//4.event

stCoEpoll_t * 	co_get_epoll_ct(); //ct = current thread 获取环境中存储定时事件的结构体

//5.hook syscall ( poll/read/write/recv/send/recvfrom/sendto )

void 	co_enable_hook_sys();  //修改当前协程的cEnableSysHook属性为true
void 	co_disable_hook_sys();  //修改当前协程的cEnableSysHook属性为false
bool 	co_is_enable_sys_hook(); //查询当前协程的cEnableSysHook属性

//6.sync
struct stCoCond_t;//信号量结构体 存储timewait添加的事件

stCoCond_t *co_cond_alloc();//分配内存
int co_cond_free( stCoCond_t * cc );//释放内存

int co_cond_signal( stCoCond_t * );//添加最近一次入栈的timedwait事件到活动事件中,co_eventloop轮询时会运行协程。
int co_cond_broadcast( stCoCond_t * );//广播 添加所有timewait事件。
int co_cond_timedwait( stCoCond_t *,int timeout_ms );//定时时间添加到epoll中,非定时事件添加到栈中。事件触发后会删除事件。

//7.share stack
stShareStack_t* co_alloc_sharestack(int iCount, int iStackSize);//分配共享栈内存

//8.init envlist for hook get/set env
void co_set_env_list( const char *name[],size_t cnt);//初始化环境变量 

void co_log_err( const char *fmt,... );//错误日志
#endif


个人理解:co_routine.h文件的作用主要是声明一些结构体和函数,用于使用库时调用。后续会介绍协成如何管理,调用栈+时间轮。以及libco的hook,co_swap的实现,方便开发者的闭包实现,个人分析,如有不足,欢迎指出。

#include "dev_i2c.h" // 接收缓冲 u8 rx_length = 0; u8 rx_complete = 0; u8 receive_data[SIZE] = {0}; // 发送缓冲 u8 tx_index = 0; u8 send_data[SIZE] = {0}; /** * @brief 中断优先级配置 * */ void NVIC_Configuration (void) { NVIC_InitTypeDef NVIC_InitStructure = {0}; NVIC_PriorityGroupConfig (NVIC_PriorityGroup_1); NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init (&NVIC_InitStructure); } /** * @brief I2C初始化 * * @param bound 时钟频率 * @param address 地址 */ void dev_i2c_init (u32 bound, u16 address) { GPIO_InitTypeDef GPIO_InitStructure = {0}; I2C_InitTypeDef I2C_InitTSturcture = {0}; RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE); RCC_APB1PeriphClockCmd (RCC_APB1Periph_I2C1, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_30MHz; GPIO_Init (GPIOC, &GPIO_InitStructure); I2C_InitTSturcture.I2C_ClockSpeed = bound; I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C; I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_16_9; I2C_InitTSturcture.I2C_OwnAddress1 = (address << 1); I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable; I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_Init (I2C1, &I2C_InitTSturcture); I2C_Cmd (I2C1, ENABLE); I2C_DMACmd (I2C1, ENABLE); I2C_ITConfig (I2C1, I2C_IT_EVT, ENABLE); } /** * @brief DMA接收初始化 * */ void dev_dma_rx_init (void) { DMA_InitTypeDef DMA_InitStructure = {0}; RCC_AHBPeriphClockCmd (RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit (DMA1_Channel7); DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&I2C1->DATAR; DMA_InitStructure.DMA_MemoryBaseAddr = (u32)receive_data; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init (DMA1_Channel7, &DMA_InitStructure); } /** * @brief 从机发送数据 * * @param bytes 数据 * @param len 数据长度 */ void dev_i2c_send_bytes (u8 *bytes, u8 len) { for (u8 i = 0; i < len; i++) { I2C_SendData (I2C1, bytes[i]); } } void I2C1_EV_IRQHandler (void) __attribute__ ((interrupt ("WCH-Interrupt-fast"))); void I2C1_EV_IRQHandler (void) { // 设备地址匹配成功 if (I2C_GetITStatus (I2C1, I2C_IT_ADDR) != RESET) { // 清除地址匹配标志位 (void)I2C_ReadRegister (I2C1, I2C_Register_STAR1); (void)I2C_ReadRegister (I2C1, I2C_Register_STAR2); // 从机接收模式 if (I2C_GetFlagStatus (I2C1, I2C_FLAG_TRA) != SET) { rx_complete = 0; rx_length = 0; // 清除DMA标志位 DMA_ClearFlag (DMA1_FLAG_TC7 | DMA1_FLAG_HT7); memset (receive_data, 0, SIZE); // 开启DMA DMA_SetCurrDataCounter (DMA1_Channel7, SIZE); DMA_Cmd (DMA1_Channel7, ENABLE); } else { tx_index = 0; I2C_SendData (I2C1, send_data[tx_index++]); } } else if (I2C_GetFlagStatus (I2C1, I2C_FLAG_TXE)) { I2C_SendData (I2C1, send_data[tx_index++]); } // 接收到主机STOP else if (I2C_GetITStatus (I2C1, I2C_IT_STOPF)) { (void)I2C_ReadRegister (I2C1, I2C_Register_STAR1); I2C1->CTLR1 |= I2C1->CTLR1; if (!rx_complete) { // 防止重复置位 rx_complete = 1; rx_length = SIZE - DMA_GetCurrDataCounter (DMA1_Channel7); DMA_Cmd (DMA1_Channel7, DISABLE); } } } #include "app_data_analysis.h" extern u8 rx_complete; extern u8 rx_length; extern u8 receive_data[SIZE]; extern u8 send_data[SIZE]; static bool app_data_analysis_check (void) { u16 length, crc16_rec, crc16_cal; if (!rx_complete) return false; // 帧头 if (receive_data[0] != FRAME_HEAD) return false; // 设备地址 if (receive_data[1] != FRAME_DES_ADDRESS) return false; // 负载长度 length = ((uint16_t)receive_data[2]) << 8; length |= ((uint16_t)receive_data[3]); // 判断负载长度是否超限 if (length > (SIZE - 7)) return false; // 获取数据中的CRC16 crc16_rec = ((uint16_t)receive_data[rx_length - 3]) << 8; crc16_rec |= ((uint16_t)receive_data[rx_length - 2]); // 计算实际数据的CRC16 crc16_cal = com_cal_crc16 (&receive_data[4], length); // 判断传输数据的CRC16是否与实际计算得出的CRC16一致 if (crc16_rec == crc16_cal) return true; return false; } void app_data_analysis_run (void) { if (app_data_analysis_check()) { rx_complete = 0; rx_length = 0; printf ("True\r\n"); } } /********************************** (C) COPYRIGHT ******************************* * File Name : main.c * Author : WCH * Version : V1.0.0 * Date : 2023/12/25 * Description : Main program body. ********************************************************************************* * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. * Attention: This software (modified or not) and binary are used for * microcontroller manufactured by Nanjing Qinheng Microelectronics. *******************************************************************************/ /* *@Note *Multiprocessor communication mode routine: *Master:USART1_Tx(PD5)\USART1_Rx(PD6). *This routine demonstrates that USART1 receives the data sent by CH341 and inverts *it and sends it (baud rate 115200). * *Hardware connection:PD5 -- Rx * PD6 -- Tx * */ #include "debug.h" #include "dev_i2c.h" #include "com_flash.h" #include "app_data_analysis.h" /********************************************************************* * @fn main * * @brief Main program. * * @return none */ int main (void) { NVIC_PriorityGroupConfig (NVIC_PriorityGroup_1); SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init (115200); printf ("SystemClk:%d\r\n", SystemCoreClock); printf ("ChipID:%08x\r\n", DBGMCU_GetCHIPID()); NVIC_Configuration(); dev_i2c_init (100000, SLAVE_ADDRESS); dev_dma_rx_init(); com_flash_init(); printf ("All Init Complete!\r\n"); while (1) { app_data_analysis_run(); } } 为什么发送了正确的数据,有时会打印输出两次True,这是CH32V003F4P6代码
最新发布
10-29
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值