02SWM181-串口
**串口:**进行人机交互的第一步!我们学会调用串口外设!
串口相关理论知识在这里就不再一一说出啦!大家阔以先普及普及一下串口知识,都是互通的知识!
在这里我想使用串口这个外设,我看了芯片的所有管脚也没有发现 “RXT,TXD”这两条外设线!于是,翻看芯片数据手册,才发现有一个芯片内部有一个神奇的==数字信号引脚分配器——>PORTCON。==
PORTCON:🤜
接下来我们细看 ”PORTCON“ ◀️:对于部分数字输入输出功能,可以配置到任意 I/O 引脚
我们举个例子:
UARTn_TX UARTn_RX
I2Cn_SDA I2Cn_CLK
PWMx_OUT PWM_BREAK重要的是要怎么使用呢!我们来看看
我们先来看张图片!
这个就是实现管脚任意化的——-数字管脚分配器!
注意!!!
意思是可用把上面列出来的数字类型引脚分配到任意的带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;
}
这次就阔以在串口助手上打印数据啦!
更换管脚
我们仅需更改组,管脚,功能,输出/输入即可!
PORT_Init(PORTB, PIN2, FUNMUX_UART0_RXD, 1); //GPIOA.0配置为UART0输入引脚 PORT_Init(PORTB, PIN3, FUNMUX_UART0_TXD, 0); //GPIOA.1配置为UART0输出引脚
哈哈哈!我们在这里算是轻松使用了,官方提供的库函数,入手一个新的单片机,先学会调用!是入手的学习好的方法!
配置串口中断
**规则:**我们一般写程序需要用到的是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)异常向量,默认是不指定中断源的,需要我们配置它的中断信号来源。(1)使能外设中断
不同的外设拥有不同的中断,具体情况看对应外设的寄存器。(2)编写中断服务函数
IRQ中断一共有32个,分别是IRQ0~IRQ31。以IRQ0为例:
(3)连接中断服务函数
①配置IRQ的中断源
②设置其优先级
③使能该中断
why???
怎么配置一个IRQ指向的是什么源呢?
还是看图,中断源的选择应该是由一个多路选择开关来决定的。
对于IRQ0~15,由IRQx_SRC 寄存器的bit[6:0]选择一个中断源:
对于IQR16~31,可由IRQx_SRC 寄存器的bit[4:0]选择中断源1,bit[9:5]选择中断源2,可用配置2个中断源:
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!