环境搭建这里就跳过了,没啥用,我还是用keil 5 开发,自行下载个pack包安装就好了。点此前往新塘官网。搜索自己的mcu型号,打开页面,在资源中有文档和软件。
在文档中下载数据手册等文档,在软件中下载例程和工具,软件中最实用的是以下几个软件:
从上到下依次是:官方例程库,nu_link驱动,外设引脚配置软件,时钟配置软件。
外设引脚配置软件用于快速配置引脚以及复用,该软件只能配置引脚及其功能,不能配置外设等功能呢,例如串口的相关配置,这些事实现不了的。
时钟配置软件仅用于配置系统时钟以及各外设时钟。这两个软件支持导出.c代码。可复制粘贴到自己的工程。
这两个软件都是非常简单的,这里就不赘述了。
但是有一个时钟配置软件有bug,以我用的m031se3ae为例,外部时钟最大可使用32m,但是软件中最大只支持24m,希望官方可以修复。
开始代码
下载官方的例程,固件库代码在文件夹:d:m031_series_bsp_cmsis_v3.03.000samplecodestddriver,寄存器代码在d:m031_series_bsp_cmsis_v3.03.000samplecoderegbased
这里采用固件库的方式开发,方便快捷。
时钟初始化:
void sys_init(void){
/*---------------------------------------------------------------------------------------------------------*/ /* init system clock */ /*---------------------------------------------------------------------------------------------------------*/ /* unlock protected registers */ sys_unlockreg();
/* enable hirc clock (internal rc 48mhz) */ clk_enablextalrc(clk_pwrctl_hircen_msk);
/* wait for hirc clock ready */ clk_waitclockready(clk_status_hircstb_msk);
/* select hclk clock source as hirc and hclk source divider as 1 */ clk_sethclk(clk_clksel0_hclksel_hirc, clk_clkdiv0_hclk(1));
/* set both pclk0 and pclk1 as hclk */ clk-》pclkdiv = clk_pclkdiv_apb0div_div1 | clk_pclkdiv_apb1div_div1;
/* select ip clock source */ /* select uart0 clock source is hirc */ clk_setmoduleclock(uart0_module, clk_clksel1_uart0sel_hirc, clk_clkdiv0_uart0(1)); /* select uart1 clock source is hirc */ clk_setmoduleclock(uart1_module, clk_clksel1_uart1sel_hirc, clk_clkdiv0_uart1(1));
/* enable uart0 peripheral clock */ clk_enablemoduleclock(uart0_module); /* enable uart1 peripheral clock */ clk_enablemoduleclock(uart1_module); /* enable pdma module clock */ clk_enablemoduleclock(pdma_module);
/* update system core clock */ /* user can use systemcoreclockupdate() to calculate pllclock, systemcoreclock and cycylesperus automatically. */ systemcoreclockupdate();
/*---------------------------------------------------------------------------------------------------------*/ /* init i/o multi-function */ /*---------------------------------------------------------------------------------------------------------*/
/* set pb multi-function pins for uart0 rxd=pb.12 and txd=pb.13 */ sys-》gpb_mfph = (sys-》gpb_mfph & ~(sys_gpb_mfph_pb12mfp_msk | sys_gpb_mfph_pb13mfp_msk)) | (sys_gpb_mfph_pb12mfp_uart0_rxd | sys_gpb_mfph_pb13mfp_uart0_txd);
/* set pb multi-function pins for uart1 rxd(pb.2) and txd(pb.3) */ sys-》gpb_mfpl = (sys-》gpb_mfpl & ~(sys_gpb_mfpl_pb2mfp_msk | sys_gpb_mfpl_pb3mfp_msk)) | (sys_gpb_mfpl_pb2mfp_uart1_rxd | sys_gpb_mfpl_pb3mfp_uart1_txd);
/* lock protected registers */ sys_lockreg();}
在初始化时钟之前需要确认自己的外部晶振的频率,然后在system_m031series.h文件的第38行修改宏定义。
在初始化时钟时会将需要的外设时钟一起初始化,这里初始化了uart0和uart1的时钟以及pdma的时钟。
初始化uart
由于时钟已经配置,在初始化uart的配置时会显得特别简单。如果你的uart没有特殊要求,两行代码即可完成uart的初始化。
void uart0_init(){ /*---------------------------------------------------------------------------------------------------------*/ /* init uart */ /*---------------------------------------------------------------------------------------------------------*/ /* reset uart0 */ sys_resetmodule(uart0_rst);
/* configure uart0 and set uart0 baud rate */ uart_open(uart0, 115200);}
void uart1_init(){ /*---------------------------------------------------------------------------------------------------------*/ /* init uart */ /*---------------------------------------------------------------------------------------------------------*/ /* reset uart1 */ sys_resetmodule(uart1_rst);
/* configure uart1 and set uart1 baudrate */ uart_open(uart1, 2500000); /* enable interrupt and install the call back function */ nvic_enableirq(uart13_irqn); uart_enableint(uart1, uart_inten_rdaien_msk); }
串口0用于printf的调试。串口1 是我需要与其他串口设备通信的接口。除了我在串口1设置了串口接收中断以外,初始化一个串口仅仅需要两个函数,非常方便,这里使用的串口默认配置:一个停止位,无校验位,8位数据,如果需要修改可自行进入函数修改。
在配置串口1的接收中断时遇到了问题:调用nvic_enableirq()函数初始化中断线时,参数我填的是uart1_irqn,无报错,编译可通过,但是测试没现象,于是进入debug页面,发现中断函数并未被编译。
尝试了很多方法都不能进行编译,后来去看uart1_irqn的定义,发现这个宏定义下面还有一个uart13_irqn。于是明白过来了,
uart0和uart2共用一个中断函数uart02_irqhandler(),uart1和3共用中断函数uart13_irqhandler()这里区别于其他家的库,不能用uart1_irqn,需要用uart13_irqn。
pdma配置
void pdma_uart_txtest(void){ /* uart tx pdma channel configuration */ /* set transfer width (8 bits) and transfer count */ pdma_settransfercnt(pdma, uart_tx_dma_ch, pdma_width_8, uart_test_length);
/* set source/destination address and attributes */ pdma_settransferaddr(pdma, uart_tx_dma_ch, (uint32_t)srcarray, pdma_sar_inc, (uint32_t)&uart1-》dat, pdma_dar_fix);
/* set request source; set basic mode. */ pdma_settransfermode(pdma, uart_tx_dma_ch, pdma_uart1_tx, false, 0);
/* single request type */ pdma_setbursttype(pdma, uart_tx_dma_ch, pdma_req_single, 0);
/* disable table interrupt */ pdma_disableint(pdma,uart_tx_dma_ch, pdma_int_tempty );}
这里有几个需要自行修改的地方,pdma_settransfercnt(pdma, uart_tx_dma_ch, pdma_width_8, uart_test_length);修改pdma_width_8为修改数据宽度,这里默认8位,uart_test_length为发送长度,我这里设置为11个。pdma_settransferaddr(pdma, uart_tx_dma_ch, (uint32_t)srcarray, pdma_sar_inc, (uint32_t)&uart1-》dat, pdma_dar_fix); srcarray为数组的地址。因为我是发送的dma,这里配置为内存到外设,如果是接收dma则做以下设置: pdma_settransferaddr(pdma, uart_rx_dma_ch, (uint32_t)&uart1-》dat, pdma_sar_fix, (uint32_t)destarray, pdma_dar_inc);
配置完后再主函数打开dma:
sys_resetmodule(pdma_rst);
pdma_open(pdma, (1 《《 uart_tx_dma_ch));
中断函数配置及启动dma
void uart13_irqhandler(void){ uint8_t res;// uint32_t u32intsts = uart1-》isr; pb1 = 0;
res = uart_read(uart1);//读uart_dat寄存器自动清除中断标志 if(res == 0x1a) { uart_disable_int(uart1, uart_inten_txpdmaen_msk); pdma_uart_txtest(); uart_enable_int(uart1, uart_inten_txpdmaen_msk); while (pdma-》dsct[uart_tx_dma_ch].ctl & pdma_dsct_ctl_txcnt_msk) ; } pb1 = 1;}
在m031中,区别于我之前用过的其他mcu,在进入中断函数之后,只要读取串口接收寄存器uart_dat中的值,便可自动清除中断标志,并不需要去操作其他寄存器。非常好用。
dma的启动和其他的mcu类似,[size=14.6667px]需重新配置传输个数,[size=14.6667px]ram[size=14.6667px]地址等,再调用一次初始化函数就行。然后利用[size=14.6667px]while (pdma-》dsct[uart_tx_dma_ch].ctl & pdma_dsct_ctl_txcnt_msk) ;判断数据是否发送完成,实际上就是等待传输个数计数器为0。
这里插一句pb1的作用,pbi就是gpio pb1口。初始化就一句话gpio_setmode(pb, bit1, gpio_mode_output);这里方便示波器观察时间。在使用库函数初始化pwm时,因为每一次的启动都要调用该函数,库函数的操作很费时间,在触发串口接收中断后将pb1拉低,发送完拉高,在示波器观察到从触发接收中断到第一个串口数据发送出去,也就是dma启动完成,大约耗时8us,效率低下。于是我将dma初始化改用寄存器的方式,时间缩小到2.8us,好用!
void pdma_uart_txtest(void){ /* uart tx pdma channel configuration */ pdma-》dsct[uart_tx_dma_ch].ctl = (uart_test_length - 1) 《《 pdma_dsct_ctl_txcnt_pos | /* transfer count */ pdma_width_8 | /* transfer width 8 bits */ pdma_dar_fix | /* fixed destination address */ pdma_sar_inc | /* increment source address */ pdma_dsct_ctl_tbintdis_msk | /* table interrupt disabled */ pdma_req_single | /* single request type */ pdma_op_basic; /* basic mode */ pdma-》dsct[uart_tx_dma_ch].sa = (uint32_t)srcarray; /* source address */ pdma-》dsct[uart_tx_dma_ch].da = (uint32_t)&uart1-》dat; /* destination address */
/* request source selection */ pdma-》reqsel0_3 = (pdma-》reqsel0_3 & (~pdma_reqsel0_3_reqsrc1_msk)) | (pdma_uart1_tx 《《 pdma_reqsel0_3_reqsrc1_pos);}
end
本文为21ic论坛蓝v作者呐咯密密原创撰写
PCB覆铜的作用、正确方法、设计
奔驰C级路试谍照曝光 配备动能回收系统
国内ESD生产厂家,可替代国际品牌对应料号
复合电压启动的过电流保护组成部分及优点
人脸识别门禁系统的主要功能及优势介绍
浅析新塘031 串口PDMA通信
ICT测试与飞针测试
电热水器保安装置制作原理
51单片机“积木式”实验板的制作
美国向苹果发出的国家安全相关数据索取要求再次急剧增长
中国铁塔:改变业主方对基站印象,让基站入场更简单
5G芯片的现在格局是怎样的
数字示波器的自动(自动设置和自动量程)详解
关于全球显示面板市场的六大发展趋势分析
Facebook宣布发布深度学习框架 PyTorch 1.0开发者预览版
GGII:国内锂电前段制浆设备市场分析
形状记忆合金的简介、原理和应用解析
Banana Pi BPI-Centi-S3 使用MicroPython编程显示JPG图片
光电式电流互感器的发展与应用
讨软件开发过程中关于角色、重构和质量的问题