Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
AB32VG1
AB32VG1-在rt-thread中如何使用串口2(uart2)
发布于 2022-03-17 11:15:02 浏览:854
订阅该版
[tocm] AB32VG1-在rt-thread中如何使用串口2(uart2) --- --- AB32VG1开发板使用的芯片是AB5301A,这个芯片的公开的资料相对比较少。官方公开的资料主要是以下3个: * 1.中科蓝讯开发板工程文档使用说明 https://ab32vg1-example.readthedocs.io/zh/latest/index.html * 2.文档更新仓库(会不定期更新):https://gitee.com/bluetrum/AB32VG1_DOC * 3.RT-Thread 官方论坛关于 AB32VG1 开发板的技术分享 : https://club.rt-thread.org/ask/search.html?module=article&type=new&keyword=ab32vg1 其他零零散散,也可以搜索到很多网友大神的教程,我重点看了《嵌入式实验楼》的例子。我这里就是以他的例子为基础进行修改的。[《 玩转中科蓝讯(AB32VG1)开发板》第6章 音乐播放器](https://club.rt-thread.org/ask/article/2740.html) --- # 一、串口2(uart2)相关寄存器 ## 1.1 串口2使用的引脚,如何配置GPIO功能复用 查看文档《AB32VG1_Register.pdf》UART2的引脚是PE2、PE3或者PB2、PB1。   我选用的是PE3 -- TX2G1 ,PE2 -- RX2G1. 通过寄存器FUNCMCON1 来配置,看看FUNCMCON1的内容:  只要在合适的地方插入下面的语句就可以实现配置。 ```c FUNCMCON1 = ((1u << 8)|(1u <<4)); ``` ## 1.2 配置GPIO复用的代码应该放在哪里 串口初始化代码在《ab32vg1_hal_msp.c》文件中,下面是修改后的代码,uart0和uart1是官方提供的,uart2是我增加上去的: ```c void hal_uart_mspinit(struct uart_handle *huart) { struct gpio_init gpio_init; if (huart->instance == UART0_BASE) { gpio_init.pin = GPIO_PIN_7; gpio_init.pull = GPIO_PULLUP; gpio_init.dir = GPIO_DIR_INPUT; gpio_init.de = GPIO_DIGITAL; gpio_init.alternate = GPIO_AF_MAP_Gx(UT0TXMAP_AF, GPIO_AF_G1) | UT0RXMAP_TX; gpio_init.af_con = GPIO_AFEN | GPIO_AFCON0 | UT0TXMAP_AF; hal_gpio_init(GPIOA_BASE, &gpio_init); } else if (huart->instance == UART1_BASE) { gpio_init.pin = GPIO_PIN_4; gpio_init.dir = GPIO_DIR_OUTPUT; gpio_init.de = GPIO_DIGITAL; gpio_init.alternate = GPIO_AF_MAP_Gx(UT1TXMAP_AF, GPIO_AF_G2); gpio_init.af_con = GPIO_AFEN | GPIO_AFCON0 | UT1TXMAP_AF; hal_gpio_init(GPIOA_BASE, &gpio_init); gpio_init.pin = GPIO_PIN_3; gpio_init.pull = GPIO_PULLUP; gpio_init.dir = GPIO_DIR_INPUT; gpio_init.de = GPIO_DIGITAL; gpio_init.alternate = GPIO_AF_MAP_Gx(UT1RXMAP_AF, GPIO_AF_G2); gpio_init.af_con = GPIO_AFEN | GPIO_AFCON0 | UT1RXMAP_AF; hal_gpio_init(GPIOA_BASE, &gpio_init); /* Interrupt */ } else if (huart->instance == UART2_BASE) {// 增加对串口2的初始化 gpio_init.pin = GPIO_PIN_3; // PE3 -- TX2G1 gpio_init.dir = GPIO_DIR_OUTPUT; gpio_init.de = GPIO_DIGITAL; gpio_init.alternate = 0; // 通过这两个参数配置GPIO复用的逻辑太复杂 gpio_init.af_con = 0; // 一时间没有搞清楚,直接不用 hal_gpio_init(GPIOE_BASE, &gpio_init); gpio_init.pin = GPIO_PIN_2; // PE2 -- RX2G1 gpio_init.pull = GPIO_PULLUP; gpio_init.dir = GPIO_DIR_INPUT; gpio_init.de = GPIO_DIGITAL; gpio_init.alternate = 0; gpio_init.af_con = 0; hal_gpio_init(GPIOE_BASE, &gpio_init); FUNCMCON1 = ((1u << 8)|(1u <<4)); // 这个是关键的代码 } } ``` 下面给出官方配置GPIO复用的代码,相关的代码分别放在几个文件,这里集中放置,供学有余力的同学参考: ```c //
/* Private constants */ #define FUNCMCONx(x) *(volatile uint32_t*)(SFR0_BASE + (0x07 + (x))*4) #define GPIO_AFDIS (0u << 7) #define GPIO_AFEN (1u << 7) #define GPIO_AFCON0 (0u << 5) /*!< When using UARTT0 UART1 HSUART SPI0 and SD0 */ #define GPIO_AFCON1 (1u << 5) /*!< When using LPWM0 LPWM1 LPWM2 LPWM3 SPI1 UART2 and CLKOUT */ #define GPIO_AFCON2 (2u << 5) /*!< When using IR TIMER3 TIMER4 TIMER5 and IIS */ #define GPIO_AFCON_MASK (0x3u << 5) #define GPIO_GET_AFCON(af_con) (uint8_t)(((af_con) & (GPIO_AFCON_MASK)) >> 5) //-------SFR0_BASE ---
--- #define SFR0_BASE (0x00000000 + 0x000) //---------------
------------------------- #define UT1RXMAP_AF (28u) #define UT1TXMAP_AF (24u) #define HSUTRXMAP_AF (20u) #define HSUTTXMAP_AF (16u) #define UT0RXMAP_AF (12u) #define UT0TXMAP_AF ( 8u) #define SPI0MAP_AF ( 4u) #define SD0MAP_AF ( 0u) #define UT1RXMAP_TX ((uint32_t)(0x3u << (UT1RXMAP_AF))) #define UT0RXMAP_TX ((uint32_t)(0x7u << (UT0RXMAP_AF))) #define GPIO_AF_MAP_Gx(AF, Gx) ((uint32_t)((Gx) << (AF))) #define GPIO_AF_MAP_CLR(AF) ((uint32_t)(0xfu << (AF))) //////////////////////////////////////////////////////////////////////////// //---------
--------------------------------------- void hal_gpio_init(hal_sfr_t gpiox, gpio_init_t gpio_init) {... gpio_afinit(gpiox, iocurrent, gpio_init->alternate, gpio_init->af_con); ... } void gpio_afinit(hal_sfr_t gpiox, uint8_t pin, uint32_t alternate, uint32_t af_con) { uint32_t is_rx_map_tx = (alternate & UT1RXMAP_TX) || (alternate & UT0RXMAP_TX); if ((af_con & 0xf0u) != GPIO_AFDIS) { gpiox[GPIOxFEN] |= BIT(pin); switch (is_rx_map_tx) { case UT1RXMAP_TX: FUNCMCONx(GPIO_GET_AFCON(af_con)) |= GPIO_AF_MAP_CLR(UT1RXMAP_AF); break; case UT0RXMAP_TX: FUNCMCONx(GPIO_GET_AFCON(af_con)) |= GPIO_AF_MAP_CLR(UT0RXMAP_AF); break; default: break; } FUNCMCONx(GPIO_GET_AFCON(af_con)) |= GPIO_AF_MAP_CLR(af_con & 0x1f); FUNCMCONx(GPIO_GET_AFCON(af_con)) |= alternate; HAL_LOG("af_con=0x%X AFCON=%d alternate=%d\n", af_con, GPIO_GET_AFCON(af_con), (af_con & 0x1f)); } } ``` # 二、串口初始化流程跟踪 ## 1.rtthread_startup() 函数rtthread_startup在《components.c》中,调用了rt_hw_board_init();  ## 2.rt_hw_board_init() 函数rt_hw_board_init在《board.c》中,调用了rt_hw_usart_init();  ## 3.rt_hw_usart_init() 函数rt_hw_usart_init在《drv_uart.c》中,drv_uart.c里面有很多处需要修改。  这里贴出drv_uart.c的全部代码,改动有几处: 1. 增加串口2的枚举 2. 增加串口2的描述和寄存器基址 3. 中断服务函数中增加串口2的接收中断服务。 ```c /* * Copyright (c) 2020-2021, Bluetrum Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2020-11-20 greedyhao first version */ #include "board.h" #include "drv_usart.h" #ifdef RT_USING_SERIAL //#define DRV_DEBUG #define LOG_TAG "drv.usart" #include
#undef RT_SERIAL_USING_DMA enum { UART0_INDEX, UART1_INDEX, UART2_INDEX, // 【1】增加串口2的枚举 }; static struct ab32_uart_config uart_config[] = { { .name = "uart0", .instance = UART0_BASE, }, { .name = "uart1", .instance = UART1_BASE, },// 【2】增加串口2的描述和寄存器基址 { .name = "uart2", .instance = UART2_BASE, } }; static struct ab32_uart uart_obj[sizeof(uart_config) / sizeof(uart_config[0])] = {0}; static rt_err_t ab32_configure(struct rt_serial_device *serial, struct serial_configure *cfg) { struct ab32_uart *uart; RT_ASSERT(serial != RT_NULL); RT_ASSERT(cfg != RT_NULL); uart = rt_container_of(serial, struct ab32_uart, serial); uart->handle.instance = uart->config->instance; uart->handle.init.baud = cfg->baud_rate; uart->handle.init.mode = UART_MODE_TX_RX; switch (cfg->data_bits) { case DATA_BITS_8: uart->handle.init.word_len = UART_WORDLENGTH_8B; break; case DATA_BITS_9: uart->handle.init.word_len = UART_WORDLENGTH_9B; break; default: uart->handle.init.word_len = UART_WORDLENGTH_8B; break; } switch (cfg->stop_bits) { case STOP_BITS_1: uart->handle.init.stop_bits = UART_STOPBITS_1; break; case STOP_BITS_2: uart->handle.init.stop_bits = UART_STOPBITS_2; break; default: uart->handle.init.stop_bits = UART_STOPBITS_1; break; } #ifdef RT_SERIAL_USING_DMA uart->dma_rx.last_index = 0; #endif hal_uart_init(&uart->handle); return RT_EOK; } static rt_err_t ab32_control(struct rt_serial_device *serial, int cmd, void *arg) { struct ab32_uart *uart; #ifdef RT_SERIAL_USING_DMA rt_ubase_t ctrl_arg = (rt_ubase_t)arg; #endif RT_ASSERT(serial != RT_NULL); uart = rt_container_of(serial, struct ab32_uart, serial); switch (cmd) { /* disable interrupt */ case RT_DEVICE_CTRL_CLR_INT: hal_uart_control(uart->handle.instance, UART_RXIT_ENABLE, HAL_DISABLE); break; /* enable interrupt */ case RT_DEVICE_CTRL_SET_INT: hal_uart_clrflag(uart->handle.instance, UART_FLAG_RXPND); hal_uart_control(uart->handle.instance, UART_RXIT_ENABLE, HAL_ENABLE); break; case RT_DEVICE_CTRL_CLOSE: hal_uart_deinit(uart->handle.instance); break; } return RT_EOK; } static int ab32_putc(struct rt_serial_device *serial, char ch) { struct ab32_uart *uart; RT_ASSERT(serial != RT_NULL); uart = rt_container_of(serial, struct ab32_uart, serial); hal_uart_clrflag(uart->handle.instance, UART_FLAG_TXPND); hal_uart_write(uart->handle.instance, ch); while(hal_uart_getflag(uart->handle.instance, UART_FLAG_TXPND) == 0); return 1; } static int ab32_getc(struct rt_serial_device *serial) { int ch; struct ab32_uart *uart; RT_ASSERT(serial != RT_NULL); uart = rt_container_of(serial, struct ab32_uart, serial); ch = -1; if(hal_uart_getflag(uart->handle.instance, UART_FLAG_RXPND) != HAL_RESET) { ch = hal_uart_read(uart->handle.instance); hal_uart_clrflag(uart->handle.instance, UART_FLAG_RXPND); } return ch; } static rt_size_t ab32_dma_transmit(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction) { return -1; } static void uart_isr(int vector, void *param) { rt_interrupt_enter(); if(hal_uart_getflag(UART0_BASE, UART_FLAG_RXPND)) //RX one byte finish { rt_hw_serial_isr(&(uart_obj[UART0_INDEX].serial), RT_SERIAL_EVENT_RX_IND); } if(hal_uart_getflag(UART1_BASE, UART_FLAG_RXPND)) //RX one byte finish { rt_hw_serial_isr(&(uart_obj[UART1_INDEX].serial), RT_SERIAL_EVENT_RX_IND); } if(hal_uart_getflag(UART2_BASE, UART_FLAG_RXPND)) //RX one byte finish { rt_hw_serial_isr(&(uart_obj[UART2_INDEX].serial), RT_SERIAL_EVENT_RX_IND); } // 【3】中断服务函数中增加串口2的接收中断服务。 rt_interrupt_leave(); } static const struct rt_uart_ops ab32_uart_ops = { .configure = ab32_configure, .control = ab32_control, .putc = ab32_putc, .getc = ab32_getc, .dma_transmit = ab32_dma_transmit }; int rt_hw_usart_init(void) { rt_size_t obj_num = sizeof(uart_obj) / sizeof(struct ab32_uart); struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; rt_err_t result = 0; rt_hw_interrupt_install(IRQ_UART0_2_VECTOR, uart_isr, RT_NULL, "ut_isr"); for (int i = 0; i < obj_num; i++) { /* init UART object */ uart_obj[i].config = &uart_config[i]; uart_obj[i].serial.ops = &ab32_uart_ops; uart_obj[i].serial.config = config; uart_obj[i].serial.config.baud_rate = 1500000; /* register UART device */ result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].config->name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX | uart_obj[i].uart_dma_flag , NULL); RT_ASSERT(result == RT_EOK); } return result; } #endif ``` ## 4. uart_config_all() 函数uart_config_all在《ab32vg1_hal_uart.c》中,这里要对uart的时钟源进行使能。 ```c void uart_config_all(struct uart_handle *huart) { hal_uart_control(huart->instance, UART_MODULE_ENABLE, HAL_DISABLE); CLKCON1 |= BIT(14); if (huart->instance == UART0_BASE) { hal_rcu_periph_clk_enable(RCU_UART0); } else if (huart->instance == UART1_BASE) { hal_rcu_periph_clk_enable(RCU_UART1); } else if (huart->instance == UART2_BASE) { // 【1】增加串口2的时钟使能 hal_rcu_periph_clk_enable(RCU_UART2); } else { return; /* Not support! */ } hal_uart_setbaud(huart->instance, huart->init.baud); if (huart->init.mode != UART_MODE_TX) { hal_uart_control(huart->instance, UART_RX_ENABLE, HAL_ENABLE); } hal_uart_control(huart->instance, UART_MODULE_ENABLE, HAL_ENABLE); } ``` 至此,驱动部分的改动完毕,下面看看应用部分的改动。 # 三、串口2应用实例 uart2实例的源代码在《uart2_app.c》  这个实例是完全按照“音乐播放器”实例中使用串口1的代码《uart_app.c》依样画葫芦。 主要功能在数据解析线程函数 data_uart2_parsing() ,完成以下动作: 1. 发送字符串“1234”共10次; 2. uart2数据转发,把收到的数据回传。 下载程序后,msh>输入 uart2_init 命令,波特率为9600. ```c #include "uart_app.h" #include "key_app.h" #include "led_app.h" #define SAMPLE_UART2_NAME "uart2" uint8_t tmp; uint8_t flag1 = 0; struct serial_configure config_uart2 = RT_SERIAL_CONFIG_DEFAULT; /* 初始化配置参数 */ /* 用于接收消息的信号量 */ static struct rt_semaphore rx2_sem; static rt_device_t serial2; void analyticald_uart2_data(void) { rt_kprintf("vol=%d\n", tmp); } /* 接收数据回调函数 */ static rt_err_t uart2_rx_ind(rt_device_t dev, rt_size_t size) { /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */ if (size > 0) { rt_sem_release(&rx2_sem); } return RT_EOK; } static char uart2_sample_get_char(void) { uint8_t ch; while (rt_device_read(serial2, 0, &ch, 1) == 0) { rt_sem_control(&rx2_sem, RT_IPC_CMD_RESET, RT_NULL); rt_sem_take(&rx2_sem, RT_WAITING_FOREVER); } return ch; } /* 数据解析线程 */ static void data_uart2_parsing(void) { char * str="1234"; // 发送10次 for (tmp = 0; tmp < 10; ++tmp) { rt_device_write(serial2, 0, str, 4); rt_thread_mdelay(500); } while (1) { tmp = uart2_sample_get_char(); rt_device_write(serial2, 0, &tmp, 1); flag1 = 1; } } int uart2_init(void) { rt_err_t ret = RT_EOK; char uart_name[RT_NAME_MAX]; //char str[] = "hello RT-Thread!\r\n"; rt_strncpy(uart_name, SAMPLE_UART2_NAME, RT_NAME_MAX); /* 查找系统中的串口设备 */ serial2 = rt_device_find(uart_name); if (!serial2) { rt_kprintf("find %s failed!\n", uart_name); return RT_ERROR; } /* step2:修改串口配置参数 */ config_uart2.baud_rate = BAUD_RATE_9600; //修改波特率为9600 config_uart2.data_bits = DATA_BITS_8; //数据位 8 config_uart2.stop_bits = STOP_BITS_1; //停止位 1 config_uart2.bufsz = 128; //修改缓冲区 buff size 为 128 config_uart2.parity = PARITY_NONE; //无奇偶校验位 /* step3:控制串口设备。通过控制接口传入命令控制字,与控制参数 */ rt_device_control(serial2, RT_DEVICE_CTRL_CONFIG, &config_uart2); /* 初始化信号量 */ rt_sem_init(&rx2_sem, "rx2_sem", 0, RT_IPC_FLAG_FIFO); /* 以中断接收及轮询发送模式打开串口设备 */ rt_device_open(serial2, RT_DEVICE_FLAG_INT_RX); /* 设置接收回调函数 */ rt_device_set_rx_indicate(serial2, uart2_rx_ind); /* 发送字符串 */ //rt_device_write(serial, 0, str, (sizeof(str) - 1)); /* 创建 serial 线程 */ rt_thread_t thread = rt_thread_create("serial2", (void (*)(void *parameter))data_uart2_parsing, RT_NULL, 2048, 5, 5); /* 创建成功则启动线程 */ if (thread != RT_NULL) { rt_thread_startup(thread); } else { ret = RT_ERROR; } return ret; } /* 导出到 msh 命令列表中 */ MSH_CMD_EXPORT(uart2_init, uart2 device sample); ``` --- # 总结 最后提供源代码,百度盘: 链接:https://pan.baidu.com/s/1tZfSWQ_6LFHhoa2aKie5Ug?pwd=tz3m 提取码:tz3m
2
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
ade
这家伙很懒,什么也没写!
文章
1
回答
0
被采纳
0
关注TA
发私信
相关文章
1
riscv32-elf-xmaker 工具的目的
2
AB32VG1 的 RTC 内部怎么知道自己是第一次上电的呢?
3
AB32VG1 蓝牙功能
4
AB32VG1 片上 flash 的库能否发布出来
5
【中科蓝汛AB32VG1】开发板是否支持USB HOST
6
有基于RT-Thread 和 AB32VG1 的蓝牙示例吗?
7
AB32VG1_RTC时钟
8
AB32 串口2.是哪个管脚
9
AB32VG1开发板的三路LPWM使用
10
使用RT-thread新建AB32VG1工程编译时报错
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
rt-smart
FAL
I2C_IIC
UART
ESP8266
cubemx
WIZnet_W5500
ota在线升级
PWM
BSP
flash
freemodbus
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
ulog
SFUD
msh
C++_cpp
MicroPython
本月问答贡献
RTT_逍遥
10
个答案
3
次被采纳
xiaorui
3
个答案
2
次被采纳
winfeng
2
个答案
2
次被采纳
三世执戟
8
个答案
1
次被采纳
KunYi
8
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
lizimu
2
篇文章
9
次点赞
swet123
1
篇文章
4
次点赞
Days
1
篇文章
4
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部