一、前言
这篇文章主要实现了串口单字节发送、printf格式化发送和通过接收中断接收数据。
用到的工具如下:
IDE:AtmelStudio 7
芯片:ATSAM4LS2C (M4内核,主频48M)
ASF版本:3.47
仿真器:JLink 便宜美国vps 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);//只是初始化串口// 初始化串口,重定向printfstdio_serial_init(USART_SERIAL, &usart_options);//串口0总中断 抢占优先级0,响应优先级0NVIC_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//接收缓存长度//串口收发fifotypedef 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?效果:
?
93864852