**从本贴开始,bsp会根据用到的资源逐渐增加完善。
本节主要实现了UART的printf重定向、ADC 查询方式的时候用、硬件SPI的使用。
一、使用到的资源
二、 从模板工程新建工程
解压缩GD32L23x_Firmware_Library_V1.0.1.zip文件,在Examples文件夹新建MyApp文件夹,复制Template文件夹所有文件到MyApp文件夹,删除多余的内容(IAR_project文件夹)。
在GD32L23x_Firmware_Library_V1.0.1文件夹新建BSP文件夹,然后在BSP中新建两个文件夹,分别命名为inc和src,用来保存新建的.h头文件和.c源文件。
三、ADC的使用
ADC的使用,在 GD32L23x_Firmware_Library_V1.0.1中有丰富的例程。这里主要借鉴Software_trigger_regular_channel_polling例程完成。
最后实现的内容是,在需要的时候采样PA1和PA2引脚上的电压。这里使用的是摇杆,在x轴和y轴分别是两个电位器,两个电位器的输出端分别接PA1和PA2。用于获取摇杆的位置。摇杆中键接PB0。
初始化主要代码如下:
void adc_config(void)
{
/* enable ADC clock */
rcu_periph_clock_enable(RCU_ADC);;
/* config ADC clock */
rcu_adc_clock_config(RCU_ADCCK_APB2_DIV6);
/* config the GPIO as analog mode */
gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_1 | GPIO_PIN_2);
/* ADC data alignment config */
adc_data_alignment_config(ADC_DATAALIGN_RIGHT);
/* ADC channel length config */
adc_channel_length_config(ADC_REGULAR_CHANNEL, 1U);
/* ADC trigger config */
adc_external_trigger_source_config(ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_NONE);
/* ADC external trigger config */
adc_external_trigger_config(ADC_REGULAR_CHANNEL, ENABLE);
/* enable ADC interface */
adc_enable();
delay_xms(1U);
/* ADC calibration and reset calibration */
adc_calibration_enable();
}
主要分三步:1)时钟;2)引脚;3)外设ADC的配置和使能。
ADC数据转换,使用软件触发,查询方式,每次一个通道。
uint16_t adc_channel_sample(uint8_t channel)
{
/* ADC regular channel config */
adc_regular_channel_config(0U, channel, ADC_SAMPLETIME_7POINT5);
/* ADC software trigger enable */
adc_software_trigger_enable(ADC_REGULAR_CHANNEL);
/* wait the end of conversion flag */
while(!adc_flag_get(ADC_FLAG_EOC));
/* clear the end of conversion flag */
adc_flag_clear(ADC_FLAG_EOC);
/* return regular channel sample value */
return (adc_regular_data_read());
}
使用:1)调用初始化函数adc_config();2)调用sample函数,采集ADC数据转换结果adc_channel_sample(),输入参数 为ADC_CHANNEL_1(对应PA1)和ADC_CHANNEL_2(对应PA2)。
四、 USART0的初始化和printf重定向
这个例程里有现成的,直接搬过来用。具体见:GD32L23x_Demo_Suites_V1.1.0\GD32L233R_EVAL_Demo_Suites\Projects\04_USART_Printf
五、 硬件SPI
5.1 GD32L233的SPI
arm-cortex M*系列内核家族经过这么些年的发展,芯片集成的硬件外设越来越多,外设功能也越来越丰富。也就意味着,在用的时候,要充分理解硬件运行机制和库函数的具体作用和功能。否则,很可能会掉坑里。
之前入手了两个模块,一直未使用,借着这次机会,就想着,继续点灯。
模块均使用MAX7219来驱动。数码管有8位,使用一个MAX7219;点阵是4位,使用4片MAX7219驱动。
MAX7219使用标准SPI协议,因此,可以使用GD32L233的硬件SPI来驱动。
老方法还是搬程序,先看固件库有什么。
固件库重点演示了GD32L233 SPI的dma、nssp、ti、中断的使用,最接近功能的是SPI_master_slave_fullduplex_polling的例程。SPI_master_slave_fullduplex_polling主要完成了eval开发板SPI0和SPI1在全双工模式下,双向数据传输,使用查询方式,最后对双方接收到的数据做对比。
This example is based on the GD32L233R-EVAL-V1.0 board, it shows SPI0 and SPI1 fullduplex
communication using polling mode. After the communication is complete, if the receive data
equals to send data, LED1 and LED2 turn on, if not, LED1 and LED2 turn off.
Connect SPI0 SCK PIN(PA5) TO SPI1 SCK PIN(PB13).
Connect SPI0 MISO PIN(PA6) TO SPI1 MISO PIN(PB14).
Connect SPI0 MOSI PIN(PA7) TO SPI1 MOSI PIN(PB15).
/* wait for transmit complete */
while(send_n < ARRAYSIZE) {
while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_TBE));
spi_i2s_data_transmit(SPI1, spi1_send_array[send_n]);
while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_TBE));
spi_i2s_data_transmit(SPI0, spi0_send_array[send_n++]);
while(RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_RBNE));
spi1_receive_array[receive_n] = spi_i2s_data_receive(SPI1);
while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE));
spi0_receive_array[receive_n++] = spi_i2s_data_receive(SPI0);
}
查询方式发送数据的代码如上,示例代码首先没有使用NSS,简单来说好比就是SPI0和SPI1的数据对拷。
那这就简单了,减法操作。
根据start板子原理图,可以看到,SPI1是可以完全使用的,PB12-PB15引脚已经引出。
SPI初始化:
void spi1_init(void )
{
spi_parameter_struct spi_init_struct;
/* enable clock */
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_SPI1);
gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_12);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
gpio_bit_set(GPIOB,GPIO_PIN_12);
/* SPI1 GPIO configuration: SCK/PB13, MISO/PB14, MOSI/PB15 */
gpio_af_set(GPIOB, GPIO_AF_6, GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
/* deinitilize SPI and the parameters */
spi_i2s_deinit(SPI1);
spi_struct_para_init(&spi_init_struct);
/* SPI0 parameter configuration */
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_init_struct.device_mode = SPI_MASTER;
spi_init_struct.frame_size = SPI_FRAMESIZE_16BIT;
spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;
spi_init_struct.nss = SPI_NSS_SOFT;
spi_init_struct.prescale = SPI_PSC_8;
spi_init_struct.endian = SPI_ENDIAN_MSB;
spi_init(SPI1, &spi_init_struct);
/* enable SPI */
spi_enable(SPI1);
}
数据发送:
void spi1_writeInt(uint16_t data_i)
{
gpio_bit_reset(GPIOB,GPIO_PIN_12);
spi_i2s_data_transmit(SPI1, data_i);
while(SET == spi_i2s_flag_get(SPI1, SPI_FLAG_TBE));//等待传输完成,传输完成之后再取消NSS
gpio_bit_set(GPIOB,GPIO_PIN_12)
}
这样就可以写测试函数了吧,比如发送一个16位的数据。或者连续发送5个16位的数据。通过逻辑分析仪观察波形看看,结果是惨不忍睹,完全认不出是SPI波形。
查看手册看SPI发送流程:
使用TBE来判断数据的发送状态的话,是可以实现流水作业的,比如DMA模式下,大量的数据一次性发送。
而在本例中,由于max7219每次发送需要有NSS的变化,地址加数据的帧格式要求,因此并不适合使用TBE标志位的发送模式。
通过阅读手册发现,SPI还有另一个标志位,TRANS,而这个标志位,用来指示传输是否完成。SPI正在传输时,TRANS为1,空闲时为0.
本文最后代码已上传至gitee,地址:GD32L233C_START_MyApp,并持续更新。
六、 结果
七、 一些结论
1、GD32L23有两个SPI,但是两个SPI的寄存器并不完全一致。
2、手册当中SPI的硬件工作模式有跟多名词,但是没有全称,对工作模式的理解会造成一定的障碍。
3、 在出现问题的时候,要多去读手册,读源码。
本文是在体验过程中的一些记录,GD32L23的硬件SPI内容有很多,本文涉及到的是最基本的SPI的工作模式和工作过程。限于条件限制,并未对GD32L23的SPI做过多研究。
欢迎跟帖讨论。
本帖最后由 wo4fisher 于 2022-1-22 23:59 编辑