02SWM181

02SWM181-串口

**串口:**进行人机交互的第一步!我们学会调用串口外设!

串口相关理论知识在这里就不再一一说出啦!大家阔以先普及普及一下串口知识,都是互通的知识!

在这里我想使用串口这个外设,我看了芯片的所有管脚也没有发现 “RXT,TXD”这两条外设线!于是,翻看芯片数据手册,才发现有一个芯片内部有一个神奇的==数字信号引脚分配器——>PORTCON。==

PORTCON:🤜

接下来我们细看 ”PORTCON“ ◀️:对于部分数字输入输出功能,可以配置到任意 I/O 引脚

我们举个例子:

 UARTn_TX  UARTn_RX
 I2Cn_SDA  I2Cn_CLK
 PWMx_OUT  PWM_BREAK

重要的是要怎么使用呢!我们来看看


我们先来看张图片!

Z8g0fn.png

这个就是实现管脚任意化的——-数字管脚分配器!

注意!!!

意思是可用把上面列出来的数字类型引脚分配到任意的带FUNCTION功能的引脚上面。哈哈哈!是不是除了电源引脚、晶振引脚、复位引脚,其余的引脚都支持配置为FUNCTION功能。实现管脚任意化!!!

在这里我们作为快速上手一个新的开发板!直接看官方库函数!

void SerialInit(void)
{
	UART_InitStructure UART_initStruct;
	
	PORT_Init(PORTA, PIN0, FUNMUX_UART0_RXD, 1);	//GPIOA.0配置为UART0输入引脚
	PORT_Init(PORTA, PIN1, FUNMUX_UART0_TXD, 0);	//GPIOA.1配置为UART0输出引脚
 	
 	UART_initStruct.Baudrate = 115200;
	UART_initStruct.DataBits = UART_DATA_8BIT;
	UART_initStruct.Parity = UART_PARITY_NONE;
	UART_initStruct.StopBits = UART_STOP_1BIT;
	UART_initStruct.RXThreshold = 3;
	UART_initStruct.RXThresholdIEn = 0;
	UART_initStruct.TXThreshold = 3;
	UART_initStruct.TXThresholdIEn = 0;
	UART_initStruct.TimeoutTime = 10;
	UART_initStruct.TimeoutIEn = 0;
 	UART_Init(UART0, &UART_initStruct);
	UART_Open(UART0);
}

这样就是实现了串口外设的初始化!是不是很简单!

PORT_Init(PORTA, PIN0, FUNMUX_UART0_RXD, 1);	//GPIOA.0配置为UART0输入引脚
PORT_Init(PORTA, PIN1, FUNMUX_UART0_TXD, 0);	//GPIOA.1配置为UART0输出引脚

**这里是重点!**我们看一下原函数!

/****************************************************************************************************************************************** 
* 函数名称: PORT_Init()
* 功能说明:	端口引脚功能选择,可用的功能见"SWM181_port.h"文件
* 输    入: PORT_TypeDef * PORTx	指定PORT端口,有效值包括PORTA、PORTB、PORTC、PORTD、PORTE	
*			uint32_t n		   		指定PORT引脚,有效值包括PIN0、PIN1、PIN2、... ... PIN14、PIN15
*			uint32_t func	   		指定端口引脚要设定的功能,其可取值见"SWM181_port.h"文件
*			uint32_t digit_in_en	数字输入使能    1为输入   0为输出
* 输    出: 无
* 注意事项: 无
******************************************************************************************************************************************/
void PORT_Init(PORT_TypeDef * PORTx, uint32_t n, uint32_t func, uint32_t digit_in_en)
{
	
	if(func > 99)
	{
		if(n <= PIN5)
		{
			PORTx->FUNMUX0 &= ~(0x1F << (n*5));
			PORTx->FUNMUX0 |= (func-100) << (n*5);
		}
		else if(n <= PIN11)
		{
			PORTx->FUNMUX1 &= ~(0x1F << ((n-6)*5));
			PORTx->FUNMUX1 |= (func-100) << ((n-6)*5);
		}
		else if(n <= PIN15)
		{
			PORTx->FUNMUX2 &= ~(0x1F << ((n-12)*5));
			PORTx->FUNMUX2 |= (func-100) << ((n-12)*5);
		}
	}
	
	switch((uint32_t)PORTx)
	{
		case ((uint32_t)PORTA):
			if(n <= PIN7)
			{
				PORTG->PORTA_SEL1 &= ~(0x03 << (n*2));
				PORTG->PORTA_SEL1 |= (func > 99 ? 1 : func) << (n*2);
			}
			else if(n <= PIN11)
			{
				PORTG->PORTA_SEL2 &= ~(0x03 << ((n-8)*2));
				PORTG->PORTA_SEL2 |= (func > 99 ? 1 : func) << ((n-8)*2);
			}
			else if(n <= PIN15)
			{
				PORTG->PORTA_SEL2 &= ~(0x07 << (8+(n-12)*3));
				PORTG->PORTA_SEL2 |= (func > 99 ? 1 : func) << (8+(n-12)*3);
			}
			break;
			
		case ((uint32_t)PORTB):
			if(n <= PIN15)
			{
				PORTG->PORTB_SEL &= ~(0x03 << (n*2));
				PORTG->PORTB_SEL |= (func > 99 ? 1 : func) << (n*2);
			}
			break;
		
		case ((uint32_t)PORTC):
			if(n <= PIN3)
			{
				PORTG->PORTC_SEL &= ~(0x03 << (n*2));
				PORTG->PORTC_SEL |= (func > 99 ? 1 : func) << (n*2);
			}
			else if(n <= PIN7)
			{
				PORTG->PORTC_SEL &= ~(0x07 << (8+(n-4)*3));
				PORTG->PORTC_SEL |= (func > 99 ? 1 : func) << (8+(n-4)*3);
			}
			break;
		
		case ((uint32_t)PORTD):
			if(n <= PIN7)
			{
				PORTG->PORTD_SEL &= ~(0x03 << (n*2));
				PORTG->PORTD_SEL |= (func > 99 ? 1 : func) << (n*2);
			}
			break;
		
		case ((uint32_t)PORTE):
			if(n <= PIN7)
			{
				PORTG->PORTE_SEL &= ~(0x03 << (n*2));
				PORTG->PORTE_SEL |= (func > 99 ? 1 : func) << (n*2);
			}
			break;
	}
	
	PORTx->INEN &= ~(0x01 << n);
	PORTx->INEN |= (digit_in_en << n);
}

在这里,我们阔以发现是对GPIO的配置,相当于STM32管脚复用的效果!阔以先不要进底层寄存器,先学会用!
这里操作的是底层的寄存器,我们不过多讲解,建议在入手一片的单片机时,不要过多关注底层!
输出重定向

!!!是不是觉得和STM32一样啦!哈哈哈,没错的一样的!

/****************************************************************************************************************************************** 
* 函数名称: fputc()
* 功能说明: printf()使用此函数完成实际的串口打印动作
* 输    入: int ch		要打印的字符
*			FILE *f		文件句柄
* 输    出: 无
* 注意事项: 无
******************************************************************************************************************************************/
int fputc(int ch, FILE *f)
{
	UART_WriteByte(UART0, ch);
	
	while(UART_IsTXBusy(UART0));
 	
	return ch;
}

这次就阔以在串口助手上打印数据啦!

ZKRJoP.png

更换管脚

我们仅需更改组,管脚,功能,输出/输入即可!

PORT_Init(PORTB, PIN2, FUNMUX_UART0_RXD, 1);	//GPIOA.0配置为UART0输入引脚
PORT_Init(PORTB, PIN3, FUNMUX_UART0_TXD, 0);	//GPIOA.1配置为UART0输出引脚

ZKRmS6.png

哈哈哈!我们在这里算是轻松使用了,官方提供的库函数,入手一个新的单片机,先学会调用!是入手的学习好的方法!

配置串口中断

**规则:**我们一般写程序需要用到的是IRQ异常,也就是中断请求。从启动文件上看,异常向量表里面有32个IRQ异常向量,命名规则为IRQx_Handler(x=0~31)。

我们先来看一下一般的中断配置方式 !!!

一般中断的流程如下:
外设产生中断–>中断管理器–>CPU
(1)外设产生中断
这款芯片能产生中断的外设有那些呢? 哈哈哈!!!我们阔以打开数据手册查看,查看数据手册是我们最好的解决方案!

   ```c
			GPIO、PWM_HALT、IIC0、IIC1、WDT、ADC0、BOD、TIMER_PULSE、DMA、
    CACHE、Flash、CAN、CMP、ADC1、HALL、UART0~3、PWM_CH0~3等等...
   ```

​ (2)中断管理器
​ 这款芯片的中断管理器怎么管理外设产生的中断信号呢?
​ 这款芯片的IRQx_Handler(x=0~31)异常向量,默认是不指定中断源的,需要我们配置它的中断信号来源。

  • IRQ0~15的中断源。

    ZKRspn.png

  • IRQ16~31的中断源可用来源于下表的任意1项或者2项(注意:可用配置2项)

ZKR45R.png

(1)使能外设中断
不同的外设拥有不同的中断,具体情况看对应外设的寄存器。

(2)编写中断服务函数

​ IRQ中断一共有32个,分别是IRQ0~IRQ31。以IRQ0为例:

(3)连接中断服务函数

​ ①配置IRQ的中断源
​ ②设置其优先级
​ ③使能该中断


why???

​ 怎么配置一个IRQ指向的是什么源呢?
还是看图,中断源的选择应该是由一个多路选择开关来决定的。
​ 对于IRQ0~15,由IRQx_SRC 寄存器的bit[6:0]选择一个中断源:

ZKRAmz.png

对于IQR16~31,可由IRQx_SRC 寄存器的bit[4:0]选择中断源1,bit[9:5]选择中断源2,可用配置2个中断源:

ZKRSY5.png

void IRQ0_Handler(void)
{
        //此处添加代码(判断中断标志,并处理)
}

官方已经实现了操作库,以串口为例:

 IRQ_Connect(IRQ0_15_UART0, IRQ0_IRQ, 1);//实现中断的绑定

这样就能将串口0的中断连接到IRQ0,并且将其优先级设置为1。(优先级可设置为0~3,其中0为最高优先级) !!!


以串口0为例,我们使能其接收中断,然后在中断中将接收的数据写入缓冲区。

​ 都有相近的,大差不差的,学习起来都很相近!

(1)使能外设时钟
(2)使能IO口时钟
(3)配置引脚功能
(4)配置串口通讯格式:波特率、数据位、停止位、校验位等…
(5)使能发送功能/接收功能
(6)使能发送中断/接收中断
(7)使能串口外设


void SerialInit(void)
{
	UART_InitStructure UART_initStruct;
	
	PORT_Init(PORTA, PIN0, FUNMUX_UART0_RXD, 1);	//GPIOA.0配置为UART0输入引脚
	PORT_Init(PORTA, PIN1, FUNMUX_UART0_TXD, 0);	//GPIOA.1配置为UART0输出引脚
 	
 	UART_initStruct.Baudrate = 115200;
	UART_initStruct.DataBits = UART_DATA_8BIT;		//数据位数
	UART_initStruct.Parity = UART_PARITY_NONE;		//校验位
	UART_initStruct.StopBits = UART_STOP_1BIT;		//停止位
	UART_initStruct.RXThreshold = 3;
	UART_initStruct.RXThresholdIEn = 1;						//接收中断(使能)
	UART_initStruct.TXThreshold = 3;
	UART_initStruct.TXThresholdIEn = 0;						//发送中断(禁止)
	UART_initStruct.TimeoutTime = 10;							//超过10个字节未收到新的数据,便触发超时中断
	UART_initStruct.TimeoutIEn = 0;								//超时中断
 	UART_Init(UART0, &UART_initStruct);
  
  IRQ_Connect(IRQ0_15_UART0, IRQ0_IRQ, 1);			//仅需添加这行代码,实现中断
  
	UART_Open(UART0);
}

接下来,我们只需要编写中断服务函数就可以啦!将我们的逻辑写到中断服务服务函数里面!!

我们将中断序列0与串口0接收中断链接起来,并设置了中断优先级!



void IRQ0_Handler(void)
{
	uint32_t DAT;
	
	if(UART_INTRXThresholdStat(UART0) || UART_INTTimeoutStat(UART0))  //判断是否触发中断
	{
		while(UART_IsRXFIFOEmpty(UART0) == 0)														//判断FIFO中数据是否为空
		{
			if(UART_ReadByte(UART0, &DAT) == 0)														//读到一个数据
			{
				printf("%d",DAT);
			}
		}
	}
}

我们这样就实现了串口的使用,与中断的链接!

IRQ0_Handler(void)
{
uint32_t DAT;

if(UART_INTRXThresholdStat(UART0) || UART_INTTimeoutStat(UART0))  //判断是否触发中断
{
	while(UART_IsRXFIFOEmpty(UART0) == 0)														//判断FIFO中数据是否为空
	{
		if(UART_ReadByte(UART0, &DAT) == 0)														//读到一个数据
		{
			printf("%d",DAT);
		}
	}
}

}


> **我们这样就实现了串口的使用,与中断的链接!**
>
> ​	会继续更新的噢!记得关注xdm!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值