作者:yinwuqing
来源:EEPW论坛
01 开箱测评周六继续上班,明天调班接着上,今天来给大家秀一下开箱靓照。开发板是昨天下班收到的,顺丰快递包装还是挺周到的,里面装好了空气袋,防止运输中撞坏。
然后摘开空气袋,有一个精美的盒子,上面标识兆易创新公司GD32与RISC-V的logo,猎豹很健壮,像是在飞奔着。
然后打开盒子,里面有我们此次用到的核心开发板,板卡属于入门RISC-V的基本类型,采用3个mini usb接口与PC端进行通讯,还配送了1根mini usb接口的数据线,这也许是后续不需要同时对这3个mini接口进行通讯吧。
正面靓照如下所示:
然后迫不及待的将排针焊接上去(当然这是自备的排针),为后续使用IO口做好准备。
再来一张焊接后背面的痕迹:
此块开发板比我想象的要小很多,没有带OLED屏,板卡四个角有预留铜柱孔,方便采用铜柱垫高,以免底面与金属物导通造成短路。该板卡自带GD-Link模块,因此无需采用第三方调试下载工具,给GD32F103C8T6来张特写。
接下来我们需要通过官方给定的SDK包,了解RISC-V架构的知识,熟悉Nuclei Studio集成开发工具的使用。Nuclei Studio是芯来科技基于Eclipse开发的一款支持RISC-V的IDE,貌似操作起来与Keil还是有点区别的哦。再回眸一下我们的核心主控MCU:
02 简单操作-点灯五一小长假不知道大伙有没有出行,疫情期间,还是居家调调开发板比较靠谱。今天与大家来分享一下入门的点灯实验。
首先我们从GD32的官网下载好针对GD32VF103开发的资源包,解压后在“GD32VF103\GD32VF103_Demo_Suites_V1.0.3\GD32VF103C_START_Demo_Suites\Docs\User Guide”目录下有《GD32VF103C-START评估板用户指南_V1.1》,我们可以根据文档来了解常用接口硬件原理,方便进一步实施我们的第一个小实验。
搭建基于芯来科技的NucleiStudio集成开发环境,首先我们得在“NucleiStudio_IDE_201909”目录下安装“jdk-8u152-windows-x64.exe”,安装jdk、jre后才能进入NucleiStudio目录下打开eclipse应用,当然前提是你的电脑之前没有安装过java开发环境。
然后进入NucleiStudio目录下打开eclipse应用,首先设置我们将来创建工程的保存路径,最好不要选择默认C盘路径,增加系统盘的负担,我这里选择到F盘。
然后新建工程,选择创建C工程,如下图所示:
然后再给工程命名,该工程将会保存到上述软件启动时弹出的设置工作目录当中。
选择好RISC-V C工程后,一路默认选择即可。
最后直到Finish按钮不再是灰色,点击Finish按钮,打开新创建的工程,直击小锤子编译一下。
没有错误,没有警告,说明工程创建没问题。然后来看看开发板上的LED1灯硬件电路原理图:
由此可知,LED1与MCU的GPIOA_7相连接,因此需要在工程中修改相对应的代码。
在Main函数中,将自动生成原始工程的四盏灯控制改成一盏灯,编译、下载。
然后我们能看到控制打印台会有下载的进度条和打印信息,log对话框中显示红色字体属正常情况,习惯使用Keil工具的伙计们可能会感到惊讶,还以为是报错了。
熟悉芯来科技的NucleiStudio集成开发工具需要慢慢养成习惯,经常使用eclipse开发的java工程师应该比较熟练咯,此次实验实现控制LED1灯以500ms的频率在不停的闪烁,现象结果如下。
03 串口功能检测根据前段时间老师的讲解,今天来分享一下基于串口的基本功能检测。首先我们需要了解一下USART的常用理论知识。通用同步异步收发器 (USART)是用于串行数据交换提供接口,数据帧可以通过全双工或半双工,同步或异步的方式进行传输。USART提供了可编程的波特率发生器,能对系统时钟进行分频产生USART发送器和接收器所需的特定频率。USART支持DMA功能,用以实现高速率的数据通信。
USART的重要引脚描述如下:
USART模块内部框图如下:
关于USART的发送器与接收器、同步通讯时序波形这里就不再赘述。demo工程中也提供了封装好的库函数,至于USART中的状态寄存器、数据寄存器、波特率寄存器、控制寄存器、保护时间和预分频器寄存器,在代码中也有相关的英文注释,这里也不再做过多讲解。关于USART的库函数汇总如下:
按照上回LED灯的简单操作,重新创建新的C工程,然后在主函数中,敲入代码:
int main(void){uint data = 'a';uint i;gd_eval_led_init(LED1);gd_eval_com_init(EVAL_COM0);while(1){gd_eval_led_on(LED1);for(i=0;i<26;i++){usart_data_transmit(EVAL_COM0,data);while(usart_flag_get(EVAL_COM0,USART_FLAG_TC)==RESET);data++;usart_data_transmit(USART0,0X0A);while(usart_flag_get(USART0,USART_FLAG_TC)==RESET);usart_data_transmit(USART0,0X0D);while(usart_flag_get(USART0,USART_FLAG_TC)==RESET);delay_1ms(300);}gd_eval_led_off(LED1);delay_1ms(500);data = 'a';}}
然后在NucleiStudio_IDE中点击编译、调试即下载到开发板中。编译通过,无报错,无警告。
将开发板的串口插针用跳线帽连接,并使用mini USB数据线与PC端连接,接线如下图所示:
此时打开电脑的设备管理器,如果已经安装了CH340的USB转串口的驱动,则会显示相对应的串口号,如果没有则显示黄色的叹号,需要安装CH340的驱动。我的串口号显示的是10,因此在SecureCRT软件中打开串口10。
SecureCRT中设置如下参数:
然后点击连接,则会打印如下信息
同样的办法,在main函数中,修改部分代码,实现通过串口工具发送数据给GD32VF103,然后GD32VF103将数据返回给PC端。
int main(void){uint i;uint data;gd_eval_led_init(LED1);gd_eval_com_init(EVAL_COM0);while(1){gd_eval_led_on(LED1);if(usart_flag_get(USART0,USART_FLAG_RBNE)!= RESET){data = usart_data_receive(USART0);usart_data_transmit(USART0,data);while(usart_flag_get(USART0,USART_FLAG_TC)==RESET);usart_data_transmit(USART0,0X0A);while(usart_flag_get(USART0,USART_FLAG_TC)==RESET);usart_data_transmit(USART0,0X0D);while(usart_flag_get(USART0,USART_FLAG_TC)==RESET);}gd_eval_led_off(LED1);delay_1ms(500);}}
然后开发板重新上电,代码编译ok,运行下载到开发板中,打开SSCOM V5.13.1串口调试助手,这里说明一下,如使用SecureCRT软件则现象不是很明显,因为它的发送窗口与输出窗口在同一界面下,因此建议采用SSCOM工具,而且不支持十六进制显示的低版本效果也不是很好,这里推荐V5.13.1版本。通过键盘在发送窗口输入相应字符,则会收到GD32VF103返回的相同字符,并加上换行回车字符。
提供ASCII码对照表如下:
根据官方提供的参考示例,关于串口的操作还有DMA应用,串口中断响应的使用。总而言之,串口是比较常见的通信接口方式,根据其通信协议,同步、异步的特性,熟悉相关寄存器的设置是非常必要的。
04 ADC功能检测今天周末,抽点时间研究了一下GD32VF103的ADC检测功能,结合视频讲解,简单谈谈ADC。ADC即模数转换,12位ADC是一种采用逐次逼近方式的模拟数字转换器。GD32VF103有18个多路复用通道,可以转换来自16个外部通道和2个内部通道的模拟信号。模拟看门狗允许应用程序来检测输入电压是否超出用户设定的高低阈值。各种通道的A/D转换可以配置成单次、连续、扫描或间断转换模式。ADC转换的结果可以按照左对齐或右对齐的方式存储在16位数据寄存器中。片上的硬件过采样机制可以通过减少来自MCU的相关计算负担来提高性能。
ADC模块框图如下图所示:
ADC带有一个前置校准功能。在校准期间,ADC计算一个校准系数,这个系数是应用于ADC内部的,它直到ADC下次掉电才无效。在校准期间,应用不能使用ADC,它必须等到校准完成。在A/D转换前应执行校准操作。通过软件设置CLB=1来对校准进行初始化,在校准期间CLB 位会一直保持1,直到校准完成,该位由硬件清0。当ADC运行条件改变后建议重新执行一次校准操作。内部的模拟校准通过设置ADC_CTL1寄存器的RSTCLB位来重置。
关于ADC的信号同步,在有两个或者两个以上的ADC模块的产品中,可以使用ADC同步模式。在ADC同步模式下,根据ADC_CTL0寄存器中SYNCM[3:0]位所选的模式,转换的启动可以是ADC0主和ADC1从的交替触发或同步触发。在同步模式下,当配置由外部事件触发的转换时,从ADC必须通过软件来配置触发来,从而避免错误的触发引起不必要的转换。此外,对于主ADC和从ADC的外部触发必须被使能。
共有以下几种模式:
独立模式
规则并行模式注
入并行模式快速
交叉模式
慢速交叉模式
交替触发模式
注入并行模式 规则并行模式
规则并行模式 交替触发模式
注入并行模式 交叉模式
在ADC同步模式下,即使DMA不用,也要将DMA置位,从ADC的转换数据可以通过主ADC 数据寄存器读取。ADC同步框图如下:
关于ADC的寄存器如下:
ADC库函数如下:
在原来的串口检测工程中,添加部分代码。
#include "gd32vf103.h"#include "gd32vf103v_eval.h"#include "systick.h"#include uint32_t adc_value[2];void rcu_config(void);void gpio_config(void);void dma_config(void);void timer_config(void);void adc_config(void);int main(void){uint i;uint data;/*配置系统时钟*/rcu_config();/*配置GPIO口 */gpio_config();/*配置TIMER*/timer_config();/*配置DMA*/dma_config();/*配置ADC*/adc_config();/*使能TIMER1*/timer_enable(TIMER1);gd_eval_led_init(LED1);/*配置EVAL_COM0*/gd_eval_com_init(EVAL_COM0);while(1){gd_eval_led_on(LED1);printf("ADC0 regular data0 = %d \r\n",adc_value[0]);delay_1ms(200);printf("ADC0 regular data0 = %4X \r\n",adc_value[0]);delay_1ms(200);printf("ADC0 regular data1 = %d \r\n",adc_value[1]);delay_1ms(200);printf("ADC0 regular data1 = %4X \r\n",adc_value[1]);delay_1ms(200);if(usart_flag_get(USART0,USART_FLAG_RBNE)!= RESET){data = usart_data_receive(USART0);usart_data_transmit(USART0,data);while(usart_flag_get(USART0,USART_FLAG_TC)==RESET);usart_data_transmit(USART0,0X0A);while(usart_flag_get(USART0,USART_FLAG_TC)==RESET);usart_data_transmit(USART0,0X0D);while(usart_flag_get(USART0,USART_FLAG_TC)==RESET);}gd_eval_led_off(LED1);delay_1ms(500);}}void rcu_config(void){/* enable GPIOA clock */rcu_periph_clock_enable(RCU_GPIOA);/* enable ADC0 clock */rcu_periph_clock_enable(RCU_ADC0);/* enable DMA0 clock */rcu_periph_clock_enable(RCU_DMA0);/* enable timer1 clock */rcu_periph_clock_enable(RCU_TIMER1);/* config ADC clock */rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);}void gpio_config(void){/* config the GPIO as analog mode */gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_1);}void dma_config(void){/* ADC_DMA_channel configuration */dma_parameter_struct dma_data_parameter;/* ADC DMA_channel configuration */dma_deinit(DMA0, DMA_CH0);/* initialize DMA data mode */dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0));dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;dma_data_parameter.memory_addr = (uint32_t)(&adc_value);dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_DISABLE;dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT;dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY;dma_data_parameter.number = 1;dma_data_parameter.priority = DMA_PRIORITY_HIGH;dma_init(DMA0, DMA_CH0, &dma_data_parameter);dma_circulation_enable(DMA0, DMA_CH0);/* enable DMA channel */dma_channel_enable(DMA0, DMA_CH0);}void timer_config(void){timer_oc_parameter_struct timer_ocintpara;timer_parameter_struct timer_initpara;/* TIMER1 configuration */timer_struct_para_init(&timer_initpara);timer_initpara.prescaler = 5399;timer_initpara.alignedmode = TIMER_COUNTER_EDGE;timer_initpara.counterdirection = TIMER_COUNTER_UP;timer_initpara.period = 9999;timer_initpara.clockdivision = TIMER_CKDIV_DIV1;timer_initpara.repetitioncounter = 0;timer_init(TIMER1,&timer_initpara);/* CH1 configuration in PWM mode1 */timer_channel_output_struct_para_init(&timer_ocintpara);timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH;timer_ocintpara.outputstate = TIMER_CCX_ENABLE;timer_channel_output_config(TIMER1, TIMER_CH_1, &timer_ocintpara);timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_1, 3999);timer_channel_output_mode_config(TIMER1, TIMER_CH_1, TIMER_OC_MODE_PWM1);timer_channel_output_shadow_config(TIMER1, TIMER_CH_1, TIMER_OC_SHADOW_DISABLE);}void adc_config(void){/* reset ADC */adc_deinit(ADC0);/* ADC mode config */adc_mode_config(ADC_DAUL_REGULAL_FOLLOWUP_FAST);/* ADC continous function enable */adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);/* ADC data alignment config */adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);/* ADC channel length config */adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 1);/* ADC regular channel config */adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_1, ADC_SAMPLETIME_55POINT5);/* ADC trigger config */adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_EXTTRIG_REGULAR_T1_CH1);/* ADC external trigger enable */adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);/* enable ADC interface */adc_enable(ADC0);/* ADC calibration and reset calibration */adc_calibration_enable(ADC0);delay_1ms(1);/* ADC DMA function enable */adc_dma_mode_enable(ADC0);}
串口打印信息如下:
接线还是与之前一样。
通过测试ADC通道0的数据采样,说明GD32VF103可配置12位、10位、8位或者6位分辨率,支持自校准,可编程采样时间,数据寄存器可配置数据对齐方式,支持规则数据转换的DMA请求。转换开始的发起有软件与硬件触发。
05 SPI与I2C通信▼ 5.1 SPI通信
SPI模块可以通过SPI协议与外部设备进行通信。串行外设接口提供了基于SPI协议的数据发送和接收功能可以工作于主机或从机模式。SPI接口支持具有硬件CRC计算和校验的全双工和单工模式。
SPI主要特征如下:
具有全双工和单工模式的主从操作;
16位宽度,独立的发送和接收缓冲区;
8位或16位数据帧格式;
低位在前或高位在前的数据位顺序;
软件和硬件NSS管理;
硬件CRC计算、发送和校验;
发送和接收支持DMA模式;
支持SPI TI模式;
支持SPI NSS脉冲模式。
SPI结构框图如下:
SPI信号线描述详情如下:
典型的工作模式概括如下:
(1)发送流程:
在完成初始化过程之后,SPI模块使能并保持在空闲状态。在主机模式下,当软件写一个数据到发送缓冲区时,发送过程开始。在从机模式下,当SCK引脚上的SCK信号开始翻转且NSS引脚电平为低发送过程开始。所以在从机模式下,应用程序必须确保在数据发送开始前,数据已经写入发送缓冲区中。
当SPI开始发送一个数据帧时,首先将这个数据帧从数据缓冲区加载到移位寄存器中,然后开始发送加载的数据。在数据帧的第一位发送之后,TBE(发送缓冲区空)位置1。TBE标志位置1说明发送缓冲区为空,此时如果需要发送更多数据,软件应该继续写SPI_DATA寄存器。在主机模式下,若想要实现连续发送功能那么在当前数据帧发送完成前软件应该将下一个数据写入SPI_DATA寄存器中。
(2)接收流程:
在最后一个采样时钟边沿之后接收到的数据将从移位寄存器存入到接收缓冲区且RBNE(接收缓冲区非空)位置1。软件通过读SPI_DATA寄存器获得接收的数据,此操作会自动清除RBNE标志位。在MRU和MRB模式中,为了接收下一个数据帧,硬件需要连续发送时钟信号,而在全双工主机模式(MFD)中仅当发送缓冲区非空时,硬件才接收下一个数据帧。
SPI的寄存器列表如下:
SPI的库函数列表如下:
SPI流程如下:
初始化时钟
配置管脚
cs管脚配置
spi参数配置
是否使能crc
使能spi
spi发送/接收
部分代码如下:
void spi_struct_para_init(spi_parameter_struct* spi_struct){/* set the SPI struct with the default values */spi_struct->device_mode = SPI_SLAVE;spi_struct->trans_mode = SPI_TRANSMODE_FULLDUPLEX;spi_struct->frame_size = SPI_FRAMESIZE_8BIT;spi_struct->nss = SPI_NSS_HARD;spi_struct->clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;spi_struct->prescale = SPI_PSC_2;}void spi_init(uint32_t spi_periph, spi_parameter_struct* spi_struct){uint32_t reg = 0U;reg = SPI_CTL0(spi_periph);reg &= SPI_INIT_MASK;/* select SPI as master or slave */reg |= spi_struct->device_mode;/* select SPI transfer mode */reg |= spi_struct->trans_mode;/* select SPI frame size */reg |= spi_struct->frame_size;/* select SPI NSS use hardware or software */reg |= spi_struct->nss;/* select SPI LSB or MSB */reg |= spi_struct->endian;/* select SPI polarity and phase */reg |= spi_struct->clock_polarity_phase;/* select SPI prescale to adjust transmit speed */reg |= spi_struct->prescale;/* write to SPI_CTL0 register */SPI_CTL0(spi_periph) = (uint32_t)reg;SPI_I2SCTL(spi_periph) &= (uint32_t)(~SPI_I2SCTL_I2SSEL);}void spi_enable(uint32_t spi_periph){SPI_CTL0(spi_periph) |= (uint32_t)SPI_CTL0_SPIEN;}void spi_disable(uint32_t spi_periph){SPI_CTL0(spi_periph) &= (uint32_t)(~SPI_CTL0_SPIEN);}uint16_t spi_crc_get(uint32_t spi_periph,uint8_t crc){if(SPI_CRC_TX == crc){return ((uint16_t)(SPI_TCRC(spi_periph)));}else{return ((uint16_t)(SPI_RCRC(spi_periph)));}}void spi_ti_mode_enable(uint32_t spi_periph){SPI_CTL1(spi_periph) |= (uint32_t)SPI_CTL1_TMOD;}void spi_ti_mode_disable(uint32_t spi_periph){SPI_CTL1(spi_periph) &= (uint32_t)(~SPI_CTL1_TMOD);}void spi_crc_error_clear(uint32_t spi_periph){SPI_STAT(spi_periph) &= (uint32_t)(~SPI_FLAG_CRCERR);}
▼ 5.2 I2C通信
接下来聊聊I2C控制总线。I2C(内部集成电路总线)模块提供了符合工业标准的两线串行制接口,可用于MCU和外部I2C设备的通讯。I2C总线使用两条串行线:串行数据线SDA和串行时钟线SCL。I2C接口模块实现了I2C协议的标速模式,快速模式以及快速模式,具备CRC计算和校验功能,支持SMBus(系统管理总线)和PMBus(电源管理总线)。此外还支持多主机I2C总线架构。I2C接口模块也支持DMA模式,可有效减轻CPU的负担。
I2C主要特征如下:
并行总线至I2C总线协议的转换及接口;
同一接口既可实现主机功能又可实现从机功能;
主从机之间的双向数据传输;
支持7位和10位的地址模式和广播寻址;
支持I2C多主机模式;
支持标速(最高100kHz),快速(最高400kHz)和快速模式(最高1MHz);
从机模式下可配置的SCL主动拉低;
支持DMA模式;
兼容SMBus2.0和PMBus;
两个中断:字节成功传输中断和错误事件中断;
可选择的PEC(报文错误校验)生成和校验。
I2C结构框图如下:
I2C总线术语:
I2C模块有两条接口线:串行数据SDA线和串行时钟SCL线。连接到总线上的设备通过这两根线互相传递信息。SDA和SCL都是双向线,通过一个电流源或者上拉电阻接到电源正极。当总线空闲时,两条线都是高电平。连接到总线的设备输出极必须是开漏或者开集,以提供线与功能。I2C总线上的数据在标准模式下可以达到100Kbit/s,在快速模式下可以达到400Kbit/s,当I2C_FMPCFG寄存器中FMPEN位被置位时,在快速模式下可达1Mbit/s。由于I2C总线上可能会连接不同工艺的设备,逻辑‘0’和逻辑‘1’的电平并不是固定的,取决于VDD的实际电平。
所有的数据传输起始于一个START结束于一个STOP。START起始位定义为S,在SCL为高时,SDA线上出现一个从高到低的电平转换。STOP结束位定义为P,在SCL为高时,SDA线上出现一个从低到高的电平转换。
I2C寄存器如下:
I2C库函数如下:
I2C流程如下:
初始化时钟
配置管脚
使能,配置I2C时钟
I2C参数配置
使能I2C
使能应答
数据发送/接收
部分代码如下:
/* I2C register bit mask */#define I2CCLK_MAX ((uint32_t)0x00000048U) /*!< i2cclk maximum value */#define I2CCLK_MIN ((uint32_t)0x00000002U) /*!< i2cclk minimum value */#define I2C_FLAG_MASK ((uint32_t)0x0000FFFFU) /*!< i2c flag mask */#define I2C_ADDRESS_MASK ((uint32_t)0x000003FFU) /*!< i2c address mask */#define I2C_ADDRESS2_MASK ((uint32_t)0x000000FEU) /*!< the second i2c address mask */void i2c_mode_addr_config(uint32_t i2c_periph, uint32_t mode,uint32_t addformat, uint32_t addr){/* SMBus/I2C mode selected */uint32_t ctl = 0U;ctl = I2C_CTL0(i2c_periph);ctl &= ~(I2C_CTL0_SMBEN);ctl |= mode;I2C_CTL0(i2c_periph) = ctl;/* configure address */addr = addr & I2C_ADDRESS_MASK;I2C_SADDR0(i2c_periph) = (addformat | addr);}void i2c_clock_config(uint32_t i2c_periph, uint32_t clkspeed, uint32_t dutycyc){uint32_t pclk1, clkc, freq, risetime;uint32_t temp;pclk1 = rcu_clock_freq_get(CK_APB1);/* I2C peripheral clock frequency */freq = (uint32_t) (pclk1 / 1000000U);if (freq >= I2CCLK_MAX) {freq = I2CCLK_MAX;}temp = I2C_CTL1(i2c_periph);temp &= ~I2C_CTL1_I2CCLK;temp |= freq;I2C_CTL1(i2c_periph) = temp;if (100000U >= clkspeed) {/* the maximum SCL rise time is 1000ns in standard mode */risetime = (uint32_t) ((pclk1 / 1000000U) + 1U);if (risetime >= I2CCLK_MAX) {I2C_RT(i2c_periph) = I2CCLK_MAX;} else if (risetime <= I2CCLK_MIN) {I2C_RT(i2c_periph) = I2CCLK_MIN;} else {I2C_RT(i2c_periph) = risetime;}clkc = (uint32_t) (pclk1 / (clkspeed * 2U));if (clkc < 0x04U) {/* the CLKC in standard mode minmum value is 4 */clkc = 0x04U;}I2C_CKCFG(i2c_periph) |= (I2C_CKCFG_CLKC & clkc);} else if (400000U >= clkspeed) {/* the maximum SCL rise time is 300ns in fast mode */I2C_RT(i2c_periph) = (uint32_t) (((freq * (uint32_t) 300U)/ (uint32_t) 1000U) + (uint32_t) 1U);if (I2C_DTCY_2 == dutycyc) {/* I2C duty cycle is 2 */clkc = (uint32_t) (pclk1 / (clkspeed * 3U));I2C_CKCFG(i2c_periph) &= ~I2C_CKCFG_DTCY;} else {/* I2C duty cycle is 16/9 */clkc = (uint32_t) (pclk1 / (clkspeed * 25U));I2C_CKCFG(i2c_periph) |= I2C_CKCFG_DTCY;}if (0U == (clkc & I2C_CKCFG_CLKC)) {/* the CLKC in fast mode minmum value is 1 */clkc |= 0x0001U;}I2C_CKCFG(i2c_periph) |= I2C_CKCFG_FAST;I2C_CKCFG(i2c_periph) |= clkc;} else {}}void i2c_ack_config(uint32_t i2c_periph, uint32_t ack){if (I2C_ACK_ENABLE == ack) {I2C_CTL0(i2c_periph) |= I2C_CTL0_ACKEN;} else {I2C_CTL0(i2c_periph) &= ~(I2C_CTL0_ACKEN);}}void i2c_master_addressing(uint32_t i2c_periph, uint32_t addr,uint32_t trandirection){/* master is a transmitter or a receiver */if (I2C_TRANSMITTER == trandirection) {addr = addr & I2C_TRANSMITTER;} else {addr = addr | I2C_RECEIVER;}/* send slave address */I2C_DATA(i2c_periph) = addr;}void i2c_enable(uint32_t i2c_periph){I2C_CTL0(i2c_periph) |= I2C_CTL0_I2CEN;}void i2c_disable(uint32_t i2c_periph){I2C_CTL0(i2c_periph) &= ~(I2C_CTL0_I2CEN);}void i2c_start_on_bus(uint32_t i2c_periph){I2C_CTL0(i2c_periph) |= I2C_CTL0_START;}void i2c_stop_on_bus(uint32_t i2c_periph){I2C_CTL0(i2c_periph) |= I2C_CTL0_STOP;}void i2c_interrupt_enable(uint32_t i2c_periph, i2c_interrupt_enum interrupt){I2C_REG_VAL(i2c_periph, interrupt) |= BIT(I2C_BIT_POS(interrupt));}void i2c_interrupt_disable(uint32_t i2c_periph, i2c_interrupt_enum interrupt){I2C_REG_VAL(i2c_periph, interrupt) &= ~BIT(I2C_BIT_POS(interrupt));}FlagStatus i2c_interrupt_flag_get(uint32_t i2c_periph,i2c_interrupt_flag_enum int_flag){uint32_t intenable = 0U, flagstatus = 0U, bufie;/* check BUFIE */bufie = I2C_CTL1(i2c_periph) & I2C_CTL1_BUFIE;/* get the interrupt enable bit status */intenable = (I2C_REG_VAL(i2c_periph, int_flag) & BIT(I2C_BIT_POS(int_flag)));/* get the corresponding flag bit status */flagstatus = (I2C_REG_VAL2(i2c_periph, int_flag)& BIT(I2C_BIT_POS2(int_flag)));if ((I2C_INT_FLAG_RBNE == int_flag) || (I2C_INT_FLAG_TBE == int_flag)) {if (intenable && bufie) {intenable = 1U;} else {intenable = 0U;}}if ((0U != flagstatus) && (0U != intenable)) {return SET;} else {return RESET;}}void i2c_interrupt_flag_clear(uint32_t i2c_periph,i2c_interrupt_flag_enum int_flag){uint32_t temp;if (I2C_INT_FLAG_ADDSEND == int_flag) {/* read I2C_STAT0 and then read I2C_STAT1 to clear ADDSEND */temp = I2C_STAT0(i2c_periph);temp = I2C_STAT1(i2c_periph);} else {I2C_REG_VAL2(i2c_periph, int_flag) &= ~BIT(I2C_BIT_POS2(int_flag));}}
SPI与I2C通信还是比较复杂,没有串口那么简单了,因为存在主从关系,官方也提供了相应的demo示例,这里不再赘述。
06 最小系统设计最后一堂课尽然是有关硬件电路设计方面的,最小系统板的设包括软硬件的整体设计。画板并不是我的专长。只是稀疏的了解硬件原理图,焊接元器件,至于画板,在学校学习过Protel99。通过该培训课程的视频教学,尝试使用了AD的20.0.13版本操作了一下PCB板的布线。
学习了Altium Designer的基本操作,也算是有所收获。不管是硬件电路设计工程师,还是软件开发工程师,工作都不容易,只能一步一个脚印才能设计出一款优秀的,值得消费者认同的产品,您说呢。基于GD32VF103开发板学习的整套课程早已更新完毕,感兴趣的坛友可以回看,重温学习教程。这是一款入门RISC-V架构学习的开发板,感兴趣网友,极力推荐使用。
扫码入群 扫码添加管理员微信加入“电子产品世界”粉丝交流群
↓↓↓↓点击,查看更多新闻