|
本帖最后由 tinnu 于 2018-9-27 19:40 编辑
(一)丰富的串口功能
首先必须承认GD的USART做得相当丰富,LIN、ModBus有类似断开帧检测功能,开起来相当有意思,不过这个阶段还展示不打算尝鲜,首要目标是事先串口普通发送和接收中断。
(二)串口使能
demo里面已经有了串口使能的示例程序:
- void EvbUart1Config(void)
- {
- rcu_periph_clock_enable(RCU_GPIOA);
- rcu_periph_clock_enable(RCU_USART1);
- /* connect port to USART1_Tx */
- gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_2);
- /* connect port to USARTx_R1 */
- gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_3);
- /* configure USART1 Tx as alternate function push-pull */
- gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP,GPIO_PIN_2);
- gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_2);
- /* configure USART2 Rx as alternate function push-pull */
- gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP,GPIO_PIN_3);
- gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_3);
- /* USART2 configure */
- usart_deinit(USART1);
- usart_baudrate_set(USART1,115200);
- usart_receive_config(USART1, USART_RECEIVE_ENABLE);
- usart_transmit_config(USART1, USART_TRANSMIT_ENABLE);
- usart_enable(USART1);
- }
复制代码
(三)printf的重定向输出,把原子的直接搬过来改一下发送函数就可以了
- //////////////////////////////////////////////////////////////////
- //加入以下代码,支持printf函数,而不需要选择use MicroLIB
- #if 1
- #pragma import(__use_no_semihosting)
- //标准库需要的支持函数
- struct __FILE
- {
- int handle;
- };
- FILE __stdout;
- //定义_sys_exit()以避免使用半主机模式
- _sys_exit(int x)
- {
- x = x;
- }
- //重定义fputc函数
- int fputc(int ch, FILE *f)
- {
- usart_data_transmit(USART1, (uint8_t)ch);
- while(RESET == usart_flag_get(USART1, USART_FLAG_TBE));
- return ch;
- }
- #endif
复制代码
(四)USART接收器
用户手册里面这样解释接收器:
当一个数据帧接收完成,USART_STAT寄存器中的RBNE置位,如果设置了USART_CTL0寄存器中相应的中断使能位RBNEIE,将会产生中断。在USART_STAT寄存器中可以观察接收状态标志。
当接收到一帧数据,而RBNE位还没有被清零,随后的数据帧将不会存储在数据接收缓冲区中。
USART_STAT 寄存器中的溢出错误标志位ORERR 将置位。如果使能DMA 并置位
USART_CTL2寄存器中ERRIE位或者置位RBNEIE,将产生中断。
在一个接收过程中,RBNE、NERR、PERR、FERR和ORERR总是同时置位。如果没有使能
DMA,软件需检查RBNE中断是否由NERR、PERR、FERR或者ORERR置位产生。[/quote]
里面涉及了很多寄存器,也可以参考:
总的来说,就是接收器完了之后,置位RBNE(接收完成)寄存器。
再看看USART接收器寄存器,发现只有9个位,也就是接收器一帧只有一个字节。
那怎么操作就很明显了:接收一个字节置位一次,把RBNEIE(中断使能位)使能之后就会进入中断服务函数!
(五)中断服务函数
那么中断服务函数是什么呢?
因为GD跟ST的库各种相似,我们可以参考一下ST的代码形式:
[quote]stm32的启动文件startup_stm32f10x_md.s中有:DCD USART1_IRQHandler
那GD的启动文件也类似的找到,在88行:
原来也是USART1_IRQHandler
接下来依然是借鉴原子的服务函数:
- #define USART_REC_LEN 200 //定义最大接收字节数 200
- #define u8 unsigned char
- #define u16 unsigned short
复制代码
- //串口1中断服务程序
- //注意,读取USARTx->SR能避免莫名其妙的错误
- u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
- u16 USART_RX_STA=0; //接收状态标记
- void USART1_IRQHandler(void) //串口1中断服务程序
- {
- u8 Res;
- if(usart_interrupt_flag_get(USART1, USART_INT_FLAG_RBNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
- {
- Res =usart_data_receive(USART1); //读取接收到的数据
- usart_interrupt_flag_clear(USART1, USART_INT_FLAG_RBNE); //清除标志位,否则不会读取下一帧
-
- if((USART_RX_STA&0x8000)==0)//接收未完成
- {
- if(USART_RX_STA&0x4000)//接收到了0x0d
- {
- if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
- else USART_RX_STA|=0x8000; //接收完成了
- }
- else //还没收到0X0D
- {
- if(Res==0x0d)USART_RX_STA|=0x4000;
- else
- {
- USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
- USART_RX_STA++;
- if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
- }
- }
- }
- }
- }
复制代码
(六)中断使能
根据上面的表格,可以发现,需要使能的控制位是RBNEIE(中断使能位);
在USART的库里面我们发现了这个函数:
因此调用方法:
- usart_interrupt_enable(USART1,USART_INT_RBNE);
复制代码
但是程序运行后发现并不能进入中断,百般思量没有头绪,又去翻了一下ST的例程,终于发现还有个NVIC的东西需要设置!
GD的库跟ST还是有些出入的,NVIC的设置形式可以借鉴systick里面的调用形式:
- NVIC_SetPriority(USART1_IRQn, 0x00U);
- NVIC_EnableIRQ(USART1_IRQn);
复制代码
接收中断成功!
补充内容 (2018-10-2 20:54):
关于最后中断分组处,值得注意:
NVIC_SetPriority、NVIC_EnableIRQ都属于弱定义的函数,在core_cm4里面。
gd有专门封装了nvic相关函数,在"gd32f3x0_misc.h"里面,nvic_irq_enable一个函数拥有以上两个函数的作用
补充内容 (2018-10-2 20:55):
此外还要用nvic_priority_group_set函数设置一下抢断优先级和子优先级
|
|