一种实现微观单线程,宏观上多线程的方法

1.

纵所周知的是,C语言是顺序程序设计的,那么在一些MCU中,例如STM32,Atmega168等等,在微观上程序都是单线程的,那么应该如何实现微观上的多线程呢?这个用到两个东西:一是中断,二是switch语句。听老夫为你细细道来。

 

2.

举个例子来说,比如我想要实现的是:MCU每2秒通过6个USART向外发送数据。一般大家首先想到的是,配置一个定时器,每2S进入一个中断函数,然后中断函数里面写入:

USART0();	//启动串口0发送
USART1();	//启动串口1发送
USART2();	//启动串口2发送
USART3();	//启动串口3发送
USART4();	//启动串口4发送
USART5();	//启动串口5发送

但是这样会存在一个问题呀。首先我可以把启动串口发送到发送数据成功分为几个部分:

1. 拉高串口读写控制端I/O,使其变为发送模式; 2. 准备要发送的数据,对数据进行组包,这一步是很耗时间的; 3. 检测串口数据是否发送完成 ; 4.在串口发送完成的时候,拉低串口读写控制端I/O,使其变为接收模式  

仅仅在串口发送的就会有两个while(1),如下图所示,这无疑拖慢了CPU的运行速度,那么应该如何优化呢?

  //--------------------启动发送-----------------------------//
	UART0_TX;                             //使串口处于发送状态
	i=0;
	while(i<30)                           //一共发送30个字节
	{
		while(!(UCSR0A & (1<<UDRE0)));    //等待 “准备数据接收”
		UDR0 = *(TX_buf_MCU+(i++));       //开始发送数据
		while( !( UCSR0A & (1<<TXC0)) );  //等待“发送完成”
		UCSR0A |= (1<<TXC0);              //发送完成后清除中断标志位
	}
	UART0_RC; //发送完毕,再次使串口处于接收状态

3.

先贴出代码,再详细说出

void PollingSending()
{
	for (uint8_t i = 0;i < 7;i ++)              //6个通道轮询发送,故循环6次
	{ 
		switch(ComState[i])                     //ComState[i]表示第i个串口所处的状态
		{
			case 0:                 
				if (PollingSending_Flag == 1)   //当2S到,PollingSending_Flag置1
				{
					ComState[i] = 1;             //跳到状态1
				}
			break;
			case 1:
				PollingSending_Flag = 0;         //清除2S标志位
				prepareData(i);		             //准备待发送数据包
				RS485_out(i);                    //拉高IO口,准备发数据
				ComState[i] = 2;
			break;
			case 2:
				USARTSendStart(i);			   	//第i个串口开始发送,此时会进入串口发送函数
				ComState[i] = 3;	
			break;
			case 3:
				if (TxComplete[i] == 1)          //发送完成
				{
					RS485_in(i);                 //拉低I0口,变为待接收状态
					ComState[i] = 0;
				}
			break;	
		}
	}	
}


ISR(USART0_TXC_vect)     //串口0的中断发送函数
{	

	if (TxLen[i] < 6)     //发送6个字节
	{
	  USART0_UDR0 =  txbuf[TxLen]; //发送一个字节后会进入发送中断,进而发送另外一个字节
	  TxLen[chn]++;
    }
	else
	{
		TxLen[i] = 0;
		TxComplete[i] = 1;	//发送完毕
	}
}




int main(void)
{

	sysinit();	//初始化
	while(1)
	{
		wdt_reset();  //复位看门狗
		PollingSending();			
	}
}

此程序的优点所在是:每一个串口都有4种状态,比如 

 USART0:ComState[0]=0  准备发送数据

 USART1:ComState[1]=3  发送完成状态

 USART2:ComState[2]=2  正在发送状态

即单线程系统每次走一步,但这一步可能USART0再走,也有可能USART1拿去了。且之前也提过了,是中断发送,系统有可能处在USART1配置发送数据包的时候,这时 USART0的UCSR0A & (1<<UDRE0) == 1 了;    //等待 “准备数据接收”  准备好了,那么系统就去发送USART0的数据了,关键在于,若下一时刻( UCSR0A & (1<<TXC0)) ;  //等待“发送完成”  还没发送完,则系统可以不用一直在while( !( UCSR0A & (1<<TXC0)) );  //等待“发送完成”,系统可以跳出中断函数,走主程序,即继续配置USART1的数据包,这样就实现了微观上单线程,而宏观上多线程的目的。

猜你喜欢

转载自blog.csdn.net/u013414501/article/details/81461776