一、前言
这篇文章主要实现了串口单字节发送、printf格式化发送和通过接收中断接收数据。
用到的工具如下:
IDE:AtmelStudio 7
芯片:ATSAM4LS2C (M4内核,主频48M)
ASF版本:3.47
仿真器:JLink v9
二、串口初始化
2.1 ASF添加USART-Serial interface和Standard serial I/O 模块
2.2 官网找到串口的快速开发指南
https://asf.microchip.com/docs/latest/sam4l/html/serial_quickstart.html
2.3 根据快速指南的说法,通常将配置放在conf_uart_serial.h里
打开conf_uart_serial.h,添加配置代码,我们这里用串口0,波特率9600,数据宽度8位等等,如下:
#define USART_SERIAL USART0 //串口0
#define USART_SERIAL_BAUDRATE 9600 //波特率
#define USART_SERIAL_CHAR_LENGTH US_MR_CHRL_8_BIT //数据位8位
#define USART_SERIAL_PARITY US_MR_PAR_NO //无奇偶校验
#define USART_SERIAL_STOP_BIT US_MR_NBSTOP_1_BIT //1位停止位
#define USART_SERIAL_RX_PIN PIN_PA11A_USART0_RXD//接收引脚
#define USART_SERIAL_RX_MUX MUX_PA11A_USART0_RXD//复用Usart0接收
#define USART_SERIAL_TX_PIN PIN_PA12A_USART0_TXD//发送引脚
#define USART_SERIAL_TX_MUX MUX_PA12A_USART0_TXD//复用Usart0发送
串口0引脚复用PA11和PA12,可以在数据手册里的GPIO功能复用表里查到。
2.4 打开main.c,添加初始化代码,usart_options 变量用到上面定义的配置,串口初始化函数用stdio_serial_init而不是usart_serial_init,这样可以重定向printf。
#include <asf.h>
#define NVIC_PriorityGroup_2 ((uint32_t)0x5) /*!< 2 bits for pre-emption priority 2 bits for subpriority */
int main (void)
{
/* Insert system clock initialization code here (sysclk_init()). */
sysclk_init();
board_init();
delay_init();
NVIC_SetPriorityGrouping(NVIC_PriorityGroup_2); //中断分组2 2位抢占,2位响应
/* Insert application code here, after the board has been initialized. */
//配置TX端口模式
ioport_set_pin_mode(USART_SERIAL_TX_PIN, USART_SERIAL_TX_MUX);
ioport_disable_pin( USART_SERIAL_TX_PIN);//关闭普通io功能
//配置RX端口模式
ioport_set_pin_mode( USART_SERIAL_RX_PIN, USART_SERIAL_RX_MUX);
ioport_disable_pin( USART_SERIAL_RX_PIN);
//串口配置选项
usart_serial_options_t usart_options = {
.baudrate = USART_SERIAL_BAUDRATE, //波特率
.charlength = USART_SERIAL_CHAR_LENGTH, //数据位宽
.paritytype = USART_SERIAL_PARITY, //奇偶校验
.stopbits = USART_SERIAL_STOP_BIT}; //停止位
//usart_serial_init(USART_SERIAL, &usart_options);//只是初始化串口
// 初始化串口,重定向printf
stdio_serial_init(USART_SERIAL, &usart_options);
//串口0总中断 抢占优先级0,响应优先级0
NVIC_SetPriority(USART0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
//使能USART0总中断
NVIC_EnableIRQ(USART0_IRQn);
//使能串口0接收中断
usart_enable_interrupt(USART_SERIAL, US_IER_RXRDY);
//使能发送
usart_enable_tx(USART_SERIAL);
//使能接收
usart_enable_rx(USART_SERIAL);
while(1)
{
}
}
三、串口发送
3.1 添加发送代码,usart_putchar函数是单字节发送,printf可以格式化打印数据,下载到板上,顺利的话能看到串口发出的数据:
while(1)
{
usart_putchar(USART_SERIAL,'a');//单字节输出
usart_putchar(USART_SERIAL,'\r');
usart_putchar(USART_SERIAL,'\n');
printf("%s:%0d\r\n","printf输出", 123);//格式化输出
delay_ms(500);
}
3.2 printf格式化输出浮点数可能会出错,例如代码:
printf("%0.2f\r\n",12.3f);
会显示f:
到社区搜索一下,原来是工具链里面优化了,变成调用整型输出了。右击工程属性->Toolchain->ARM/GNU C Compiler->Symbols,找到scanf=iscanf和printf=iprintf删除,重新编译再试就可以了。
四、串口接收
4.1 先定义一个接收fifo结构体变量
#define TX_BUFFER_SIZE 128 //发送缓存长度
#define RX_BUFFER_SIZE 128 //接收缓存长度
//串口收发fifo
typedef struct
{
//volatile uint16_t rxcount; // 接收计数
volatile uint16_t rxindex; // 接收指针
volatile uint16_t rdindex; // 读指针
volatile uint8_t txcount; // 发送计数
volatile uint8_t txindex; // 发送指针
volatile uint8_t wrindex; // 写指针
volatile uint8_t txsbuf[TX_BUFFER_SIZE]; // 发送数据缓冲区
volatile uint8_t rxsbuf[RX_BUFFER_SIZE]; // 接收数据缓冲区
} Uart_fifo;
Uart_fifo uart_fifo;//
4.2 编写串口0中断函数,初始化串口时使能了接收中断,所以当RXRDY为1时会触发接收中断,获取收到的字符可以用usart_getchar库函数,但是里面有个while,这里我们为了防止未知错误导致死循环,直接读取数据寄存器RHR,并且读完会自动清除RXRDY位,不用手动清除。
//串口0中断函数
void USART0_Handler(void)
{
U32 u32_uart_status = usart_get_status(USART_SERIAL);
/*------------------------- 接收中断处理 ------------------------------*/
if (u32_uart_status & US_CSR_RXRDY)//判断接收标志
{
//usart_getchar(USART_SERIAL,&uart_fifo.rxsbuf[uart_fifo.rxindex]);
uart_fifo.rxsbuf[uart_fifo.rxindex] = (USART0->US_RHR & US_RHR_RXCHR_Msk);//读取数据,会自动清除接收标志
if (++uart_fifo.rxindex==RX_BUFFER_SIZE) //接收指针到末尾
{
uart_fifo.rxindex=0; //指针置0,从头开始
}
if(uart_fifo.rxindex>=3)
{
Rx_Process();//接收处理
}
}
}
4.3 接收处理,接收到3个字符以上开始判断指令,并回复。
//接收处理
void Rx_Process(void)
{
if(uart_fifo.rxsbuf[0]==0xAA && uart_fifo.rxsbuf[1]==0x01 && uart_fifo.rxsbuf[2]==0x01)
{
printf("开灯\r\n");
}
else if(uart_fifo.rxsbuf[0]==0xAA && uart_fifo.rxsbuf[1]==0x01 && uart_fifo.rxsbuf[2]==0x00)
{
printf("关灯\r\n");
}
else
{
printf("错误\r\n");
}
uart_fifo.rxindex=0; //指针置0,从头开始
}
4.4 效果: