3. SPI pit diary (+GD32L233 USART, ADC, SPI usage) using hardware SPI driver max7219
[Copy link]
This post was last edited by wo4fisher on 2022-1-22 23:59
**Starting from this post, bsp will be gradually increased and improved according to the resources used.
This section mainly implements the printf redirection of UART, the use of ADC query mode, and the use of hardware SPI.
1. Resources used
2. Create a new project from a template project
Unzip the GD32L23x_Firmware_Library_V1.0.1.zip file, create a MyApp folder in the Examples folder, copy all the files in the Template folder to the MyApp folder, and delete the redundant contents (IAR_project folder).
Create a new BSP folder in the GD32L23x_Firmware_Library_V1.0.1 folder, and then create two new folders in the BSP, named inc and src, to save the newly created .h header files and .c source files.
3. Use of ADC
There are many routines for using ADC in GD32L23x_Firmware_Library_V1.0.1. Here we mainly refer to the Software_trigger_regular_channel_polling routine.
The final implementation is to sample the voltage on the PA1 and PA2 pins when needed. Here we use a joystick, with two potentiometers on the x-axis and y-axis, and the output ends of the two potentiometers are connected to PA1 and PA2 respectively. This is used to obtain the position of the joystick. The joystick is connected to PB0.
The main initialization code is as follows:
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();
}
It is mainly divided into three steps: 1) clock; 2) pin; 3) configuration and enabling of peripheral ADC.
ADC data conversion, using software trigger, query mode, one channel at a time.
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());
}
Usage: 1) Call the initialization function adc_config(); 2) Call the sample function to collect the ADC data conversion results adc_channel_sample(), with the input parameters being ADC_CHANNEL_1 (corresponding to PA1) and ADC_CHANNEL_2 (corresponding to PA2).
4. USART0 initialization and printf redirection
This example has a ready-made one, just move it here and use it. For details, see: GD32L23x_Demo_Suites_V1.1.0\GD32L233R_EVAL_Demo_Suites\Projects\04_USART_Printf
5. Hardware SPI
5.1 SPI of GD32L233
After so many years of development, the arm-cortex M* series core family has integrated more and more hardware peripherals and richer peripheral functions. This means that when using it, you must fully understand the specific role and function of the hardware operation mechanism and library functions. Otherwise, you may fall into a pit.
I bought two modules before but never used them. I wanted to take this opportunity to continue lighting them.
The modules are all driven by MAX7219. The digital tube has 8 bits, using one MAX7219; the dot matrix has 4 bits, using 4 MAX7219 drivers.
MAX7219 uses the standard SPI protocol, so it can be driven by the hardware SPI of GD32L233.
The old method is to move the program and see what is in the firmware library first.
The firmware library focuses on demonstrating the use of DMA, NSSP, TI, and interrupts of GD32L233 SPI. The closest function is the routine of SPI_master_slave_fullduplex_polling. SPI_master_slave_fullduplex_polling mainly completes the bidirectional data transmission of SPI0 and SPI1 of the eval development board in full-duplex mode, using the query method, and finally compares the data received by both parties.
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);
}
The code for sending data in query mode is as above. The sample code does not use NSS. In simple terms, it is like copying data between SPI0 and SPI1.
That's easy, just subtraction.
According to the start board schematic, we can see that SPI1 is fully usable and the PB12-PB15 pins have been brought out.
SPI initialization:
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);
}
Data sending:
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)
}
Now we can write a test function, for example, send a 16-bit data, or send 5 16-bit data in succession. Observe the waveform through the logic analyzer, the result is terrible, it is totally unrecognizable as an SPI waveform.
Check the manual to see the SPI sending process:
If TBE is used to determine the data transmission status, pipeline operations can be implemented. For example, in DMA mode, a large amount of data can be sent at one time.
In this example, since the max7219 needs to change NSS every time it sends, and the frame format of address plus data is required, it is not suitable for the sending mode using the TBE flag.
By reading the manual, I found that SPI has another flag, TRANS, which is used to indicate whether the transmission is complete. When SPI is transmitting, TRANS is 1, and when it is idle, it is 0.
The final code of this article has been uploaded to gitee, address: , and will be continuously updated.
6. Results
VII. Some Conclusions
1. GD32L23 has two SPIs, but the registers of the two SPIs are not completely consistent.
2. There are many terms for the hardware working mode of SPI in the manual, but there is no full name, which will cause certain obstacles to the understanding of the working mode.
3. When problems arise, read the manual and source code more often.
This article is a record of some experiences. GD32L23 has a lot of hardware SPI content. This article involves the most basic SPI working mode and working process. Due to limited conditions, I have not done much research on GD32L23's SPI.
Welcome to post and discuss.
|