注意:GD32F450 总共有三种封装形式,本文所述的相关代码和知识,均为 GD32F450IX 系列。
1. 相关知识
2. 烧写程序
注意:使用SWD模式烧写的时候,开发板要单独供电。
3. SPI
3.1 spi基础
SPI1和SPI2支持全双工模式的主从操作;
SPI5支持QSPI(四线SPI)主模式,适合高速数据传输或连接外部存储器(如SPI Flash);
SPI1和SPI2的时钟源为APB1总线(最大50MHz),实际SPI时钟频率为APB1分频后的25MHz;
SPI0、SPI3、SPI4、SPI5的时钟源为APB2总线(最大100MHz),支持最高50MHz的SPI时钟频率
注意 下面所说引脚为 GD32F450IX 系类的引脚。同时考虑到SWD下载功能要一直使用。所以PA13(SWDIO) PA14(SWCLK)的引脚不要使用
3.2 spi代码
注意注意:如果兄弟们遇到了,连续读出错的情况,可以参考我下面的代码,看看是不是遇到了同样的问题!
看来网上好多spi的读写算法,发现写是没啥问题的,但是连续读经常出错,这个问题搞了好久,后来在一篇收费的博文里看到:由于发送和接收是同时进行的,而且要接收一个数据时必须在有效的SCK下,而只有数据发送才能产生有效的SCK,,所以接收数据的函数时在发送数据的函数的基础上,将发送的数据设置为0x00或者0xFF来骗取有效的SCK; 就是按照这个思路,才解决我的连续读出错的问题。
所以把自己可以用的读的程序分享出来,希望不会在有人踩坑了!
void spi1_init(void)
{
// 启用时钟
rcu_periph_clock_enable(RCU_GPIOB); // 启用GPIOB时钟
rcu_periph_clock_enable(RCU_SPI1); // 启用SPI1时钟
// 配置GPIO引脚
// PB12(SPI1_NSS), PB13(SPI1_SCK), PB15(SPI1_MOSI) 复用推挽输出 PB14(SPI1_MISO) 浮空输入
gpio_af_set(GPIOB, GPIO_AF_5, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); // AF6为SPI1功能
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_15);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_15);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14); // MISO
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_14); // 输入模式无需设置输出选项,但库函数需要
// 配置SPI参数
spi_parameter_struct spi_init_struct;
spi_struct_para_init(&spi_init_struct);
spi_init_struct.device_mode = SPI_MASTER; // 主模式
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; // 全双工
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; // 8位数据
spi_init_struct.nss = SPI_NSS_SOFT; // 软件NSS
spi_init_struct.endian = SPI_ENDIAN_MSB; // MSB优先
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_2EDGE; // CPOL=0, CPHA=1
spi_init_struct.prescale = SPI_PSC_16; // 预分频(根据系统时钟调整)
spi_init(SPI1, &spi_init_struct);
// 使能SPI
spi_enable(SPI1);
}
void spi1_send(uint8_t data) {
while (spi_i2s_flag_get(SPI1, SPI_FLAG_TBE) == RESET); /* 等待 SPI1 TX 缓存为空 */
spi_i2s_data_transmit(SPI1, data); /* 发送数据 */
while(SET == spi_i2s_flag_get(SPI1, SPI_FLAG_TRANS));/* 等待数据传输完毕 */
}
void spi1_receive(uint8_t *rx_data, uint8_t data_length) {
uint16_t i;
int rx_buffer_index = 0; // 清空接收缓冲区索引
// gpio_bit_write(GPIOB, LHA7878_SPI2_CS_Pin, GPIO_PIN_RESET); // 软件NSS拉低 (如果使用软件NSS)
for (i = 0; i < data_length; i++) {
spi1_send( 0xff); // 发送哑字节,以启动数据接收
while (spi_i2s_flag_get(SPI1, SPI_FLAG_RBNE) == RESET); // 等待接收缓冲区非空
rx_data = spi_i2s_data_receive(SPI1); // 从SPI接收数据
}
//gpio_bit_write(GPIOB, LHA7878_SPI2_CS_Pin, GPIO_PIN_RESET); // 软件NSS拉高 (如果使用软件NSS)
}
4. 串口
注意!如果要重定义printf(),一定要勾选上使用microLIB库!!!
4.1 串口引脚
4.2 串口通信代码
串口通信代码
void usart0_config(void) {
// 1. 使能时钟
rcu_periph_clock_enable(RCU_GPIOB); // 使能 GPIOB 时钟
rcu_periph_clock_enable(RCU_USART0); // 使能 USART0 时钟
// 2. 配置 GPIO 复用模式
// PB6: USART0_TX (AF7), PB7: USART0_RX (AF7)
gpio_af_set(GPIOB, GPIO_AF_7, GPIO_PIN_6 | GPIO_PIN_7);
// 3. 配置 TX (PB6) 为复用推挽输出
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_6);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
// 4. 配置 RX (PB7) 为浮空输入(或上拉输入)
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
// //配置复用功能
// gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_9); //配置PA9为复用类别7
// gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_10);
// //配置引脚的模式
// gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP,GPIO_PIN_9);//配置pa9为复用上拉模式
// gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_9);//配置pa9为推挽输出,速度为50M
// gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP,GPIO_PIN_10);
// 5. 配置 USART0 参数
usart_deinit(USART0);
usart_baudrate_set(USART0, 115200U); // 波特率 115200
usart_word_length_set(USART0, USART_WL_8BIT); // 8 位数据
usart_stop_bit_set(USART0, USART_STB_1BIT); // 1 停止位
usart_parity_config(USART0, USART_PM_NONE); // 无校验
usart_receive_config(USART0, USART_RECEIVE_ENABLE); // 使能接收
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); // 使能发送
usart_enable(USART0); // 使能 USART0
}
void usart_send_char(char c) {
while (usart_flag_get(USART0, USART_FLAG_TBE) == RESET); // 等待发送缓冲区为空
usart_data_transmit(USART0, (uint8_t)c); // 发送字符
}
// 发送一个字符串
void usart_send_string(const char *str) {
while (*str) {
usart_send_char(*str++);
}
}
// 重定义print
int fputc(int ch, FILE *f)
{
usart_data_transmit(USART0, (uint8_t)ch); //调用串口发送函数
while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));//等待发送完成
return ch;
}
5. DMA
基础知识
GD32F4上有两个DMA控制器(DMA0,DMA1)共有16个通道。每个通道可以被分配给一个或多个特定的外设进行数据传输。两个内置的总线仲裁器用来处理DMA请求的优先级问题。
16 个通道(每个 DMA 控制器有 8 个通道),每个通道连接 8 个特定的外设请求。
常用函数
void dma_init(uint32_t dma_peripheral_base_addr, uint32_t dma_channel,
uint32_t direction, uint32_t periph_data_size,
uint32_t mem_data_size, uint32_t mode,
uint32_t mem_address, uint32_t periph_address,
uint32_t buffer_size);
dma_single_data_mode_init()
// 用于初始化指定的DMA通道以进行单次数据传输模式的配置
dma_single_data_parameter_struct dma_init_struct;
// 初始化DMA参数结构体
dma_struct_para_init(&dma_init_struct); // 使用库函数初始化结构体为默认值
// 配置DMA参数
dma_init_struct.direction = DMA_PERIPH_TO_MEMORY;
dma_init_struct.memory0_addr = (uint32_t)usart5_recv_buf; // 目标缓冲区地址
dma_init_struct.periph_addr = (uint32_t)&USART_DATA(USART5); // 外设数据寄存器地址
dma_init_struct.number = 128; // 要传输的数据量
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.periph_width = DMA_PERIPH_WIDTH_8BIT;
// 初始化DMA通道
dma_single_data_mode_init(DMA1, DMA_CH2, &dma_init_struct);
dma_circulation_enable();
// 启用循环模式
dma_circulation_enable(DMA1, DMA_CH2);
dma_channel_enable()
// dma_peripheral_base_addr:DMA控制器的外设地址
// dma_channel:指定启用的DMA通道(例如,DMA1_CHANNEL1,DMA2_CHANNEL2)
void dma_channel_enable(uint32_t dma_peripheral_base_addr, uint32_t dma_channel);
dma_channel_disable()
// dma_peripheral_base_addr:DMA控制器的外设地址。
// dma_channel:要禁用的DMA通道
void dma_channel_disable(uint32_t dma_peripheral_base_addr, uint32_t dma_channel);
dma_flag_get()
// 检查指定DMA通道的状态标志(如传输完成标志)
// dma_peripheral_base_addr:DMA控制器的外设地址。
// dma_channel:DMA通道。
// dma_flag:状态标志,如DMA_FLAG_FTF(传输完成标志)或DMA_FLAG_HTF(半传输完成标志)
// 返回值:SET:标志被设置。RESET:标志未设置。
FlagStatus dma_flag_get(uint32_t dma_peripheral_base_addr, uint32_t dma_channel, uint32_t dma_flag);
dma_interrupt_flag_get()
// 检查DMA中断标志,判断DMA传输是否完成
// dma_peripheral_base_addr:DMA控制器的外设地址。
//dma_channel:DMA通道。
// dma_interrupt_flag:DMA中断标志,例如DMA_INT_FLAG_FTF(传输完成中断)。
FlagStatus dma_interrupt_flag_get(uint32_t dma_peripheral_base_addr, uint32_t dma_channel, uint32_t dma_interrupt_flag);
dma_interrupt_enable()
// dma_peripheral_base_addr:DMA控制器的外设地址。
// dma_channel:DMA通道。
// dma_interrupt:需要启用的DMA中断类型(例如DMA_INT_FTF)。
void dma_interrupt_enable(uint32_t dma_peripheral_base_addr, uint32_t dma_channel, uint32_t dma_interrupt);
dma_interrupt_flag_clear()
// 清除DMA通道的中断标志。
// dma_peripheral_base_addr:DMA控制器的外设地址。
// dma_channel:DMA通道。
// dma_interrupt_flag:需要清除的DMA中断标志。
void dma_interrupt_flag_clear(uint32_t dma_peripheral_base_addr, uint32_t dma_channel, uint32_t dma_interrupt_flag);
dma_memory_address_config()
// 配置DMA的内存地址。
// dma_peripheral_base_addr:DMA控制器的外设地址。
// dma_channel:DMA通道。
// memory_address:内存地址,通常是数据的源或目标地址。
void dma_memory_address_config(uint32_t dma_peripheral_base_addr, uint32_t dma_channel, uint32_t memory_address);
dma_peripheral_address_config()
// 配置DMA的外设地址。
// dma_peripheral_base_addr:DMA控制器的外设地址。
// dma_channel:DMA通道。
// peripheral_address:外设的地址,通常是外设寄存器的地址。
void dma_peripheral_address_config(uint32_t dma_peripheral_base_addr, uint32_t dma_channel, uint32_t peripheral_address);
dma_deinit()
// 重置DMA通道的配置,恢复默认状态。
// dma_peripheral_base_addr:DMA控制器的外设地址。
// dma_channel:需要重置的DMA通道。
void dma_deinit(uint32_t dma_peripheral_base_addr, uint32_t dma_channel);
6. DMA示例代码
6.1 DMA-USART示例代码
注意:DMA中断响应的条件是数据传输完成,跟我们设置的传输数据量是密切相关的,如果传输数据量设置的很大,但是你测试的时候,又是使用串口传输助手传输几字节,就会像我一样,出现DMA中断未响应的现象!
在绑定关联子外设的时候,对应的 DMA_SUBPERIx 的值为通道外设的值。
dma_channel_subperipheral_select(DMA1, DMA_CH2, DMA_SUBPERI5);
void usart5_config(void) {
// 1. 使能时钟
rcu_periph_clock_enable(RCU_GPIOG); // 使能 GPIOB 时钟
rcu_periph_clock_enable(RCU_USART5); // 使能 USART0 时钟
// 2. 配置 GPIO 复用模式
gpio_af_set(GPIOG, GPIO_AF_8, GPIO_PIN_9 | GPIO_PIN_14);
// 3. 配置 TX (PG14) 为复用推挽输出
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_14);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_14);
// 4. 配置 RX (PG9) 为浮空输入(或上拉输入)
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
// 5. 配置 USART0 参数
usart_deinit(USART5);
usart_baudrate_set(USART5, 115200U); // 波特率 115200
usart_word_length_set(USART5, USART_WL_8BIT); // 8 位数据
usart_stop_bit_set(USART5, USART_STB_1BIT); // 1 停止位
usart_parity_config(USART5, USART_PM_NONE); // 无校验
usart_receive_config(USART5, USART_RECEIVE_ENABLE); // 使能接收
usart_transmit_config(USART5, USART_TRANSMIT_ENABLE); // 使能发送
usart_enable(USART5);
usart_interrupt_enable(USART5, USART_INT_RBNE); // 启用接收中断
nvic_irq_enable(USART5_IRQn, 1, 0); // 启用USART5中断// 使能 USART5
usart_dma_receive_config(USART5, USART_RECEIVE_DMA_ENABLE); // 使能DMA接收功能
}
void dma_init(void){
dma_single_data_parameter_struct dma_init_struct;
// 启用DMA1的时钟,确保DMA1可以正常工作
rcu_periph_clock_enable(RCU_DMA1);
// 启用DMA1通道2的中断,使能优先级0,子优先级也为0
nvic_irq_enable(DMA1_Channel2_IRQn, 0, 0);
// 重置DMA1通道2,确保配置前清除所有旧设置
dma_deinit(DMA1, DMA_CH2);
// 初始化DMA初始化结构体成员
dma_init_struct.direction = DMA_PERIPH_TO_MEMORY; // 设置传输方向:从外设到内存
dma_init_struct.memory0_addr = (uint32_t)usart5_recv_buf; // 指定内存地址(接收缓冲区)
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; // 启用内存地址自增
dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; // 设置数据宽度为8位
dma_init_struct.number = 4; // 设置传输数据量
dma_init_struct.periph_addr = (uint32_t)&USART_DATA(USART5); // 外设地址指向USART5的数据寄存器
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; // 禁止外设地址自增
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; // 设置DMA通道优先级为超高
dma_init_struct.circular_mode = DMA_CIRCULAR_MODE_ENABLE; // 启用循环传输模式
// 使用指定的参数初始化DMA1通道2
dma_single_data_mode_init(DMA1, DMA_CH2, &dma_init_struct);
// 配置DMA通道关联的子外设,这里选择的是与USART5_RX相关的子外设
dma_channel_subperipheral_select(DMA1, DMA_CH2, DMA_SUBPERI5);
// 启用DMA传输完成中断,以便在传输完成后触发中断处理程序
dma_interrupt_enable(DMA1, DMA_CH2, DMA_INT_FTF);
// 清除可能存在的残留传输完成标志,避免错误触发中断
dma_flag_clear(DMA1, DMA_CH2, DMA_FLAG_FTF);
// 最后启用DMA1通道2,开始进行DMA传输
dma_channel_enable(DMA1, DMA_CH2);
}
// DMA中断处理函数
void DMA1_Channel2_IRQHandler(void) {
if (dma_interrupt_flag_get(DMA1, DMA_CH2, DMA_INT_FLAG_FTF)) { // 检查传输完成标志
// 先清除中断标志
dma_interrupt_flag_clear(DMA1, DMA_CH2, DMA_INT_FLAG_FTF);
printf("dma trant over\n");
flag++;
}
}
6.2 DMA-SPI示例代码
SPI配置
void spi1_init(void)
{
// 启用时钟
rcu_periph_clock_enable(RCU_GPIOB); // 启用GPIOB时钟
rcu_periph_clock_enable(RCU_SPI1); // 启用SPI1时钟
// 配置GPIO引脚 PB12(SPI1_NSS), PB13(SPI1_SCK), PB15(SPI1_MOSI) 复用推挽输出 PB14(SPI1_MISO) 浮空输入
gpio_af_set(GPIOB, GPIO_AF_5, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); // AF5为SPI1功能
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_15);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_15);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14); // MISO
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_14); // 输入模式无需设置输出选项,但库函数需要
// 配置SPI参数
spi_parameter_struct spi_init_struct;
spi_struct_para_init(&spi_init_struct);
spi_init_struct.device_mode = SPI_MASTER; // 主模式
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; // 全双工
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; // 8位数据
spi_init_struct.nss = SPI_NSS_SOFT; // 硬件NSS
spi_init_struct.endian = SPI_ENDIAN_MSB; // MSB优先
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_2EDGE; // CPOL=0, CPHA=1
spi_init_struct.prescale = SPI_PSC_4; // 预分频(根据系统时钟调整)
spi_init(SPI1, &spi_init_struct);
// 使能SPI
spi_enable(SPI1);
}
DMA读写配置;
注意:本次实验,我配置的DMA为主机,原来是只需要接收从机的数据就行了。但在GD32F450-SPI通讯过程中,时钟频率来自于主机,所以即使是只读从机的数据,还是需要发送一些数据;
由于发送和接收是同时进行的,而且要接收一个数据时必须在有效的SCK下,而只有数据发送才能产生有效的SCK,,所以接收数据的函数时在发送数据的函数的基础上,将发送的数据设置为0x00或者0xFF来骗取有效的SCK;
uint8_t ADC_2[ADC_CNT][27] = {0x00};
void dma_rx_init(void){
dma_single_data_parameter_struct dma_init_struct;
// 启用DMA1的时钟,确保DMA1可以正常工作
rcu_periph_clock_enable(RCU_DMA0);
// 启用DMA0 通道3的中断,使能优先级0,子优先级也为0
nvic_irq_enable(DMA0_Channel3_IRQn, 0, 0);
// 重置DMA1通道2,确保配置前清除所有旧设置
dma_deinit(DMA_SPI_PORT, DMA_SPI_RX_CHANNEL);
// 初始化DMA初始化结构体成员
dma_init_struct.direction = DMA_PERIPH_TO_MEMORY; // 设置传输方向:从外设到内存
dma_init_struct.memory0_addr = (uint32_t)ADC_2[0]; // 指定内存地址(接收缓冲区)
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; // 启用内存地址自增 DMA_MEMORY_INCREASE_ENABLE
dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; // 设置数据宽度为8位
dma_init_struct.number = 27; // 设置传输数据量
dma_init_struct.periph_addr = (uint32_t)&SPI_DATA(SPI1); // 外设地址指向 SPI1 的数据寄存器
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; // 禁止外设地址自增
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; // 设置DMA通道优先级为超高
dma_init_struct.circular_mode = DMA_CIRCULAR_MODE_DISABLE; // 启用循环传输模式
// 使用指定的参数初始化DMA1通道2
dma_single_data_mode_init(DMA_SPI_PORT, DMA_SPI_RX_CHANNEL, &dma_init_struct);
// 配置DMA通道关联的子外设,这里选择的是与 SPI1_RX 相关的子外设
dma_channel_subperipheral_select(DMA_SPI_PORT, DMA_SPI_RX_CHANNEL, DMA_SUBPERI0);
// 启用DMA传输完成中断,以便在传输完成后触发中断处理程序
dma_interrupt_enable(DMA_SPI_PORT, DMA_SPI_RX_CHANNEL, DMA_INT_FTF);
// 清除可能存在的残留传输完成标志,避免错误触发中断
dma_flag_clear(DMA_SPI_PORT, DMA_SPI_RX_CHANNEL, DMA_FLAG_FTF);
// 最后启用DMA0 通道3,开始进行DMA传输
dma_channel_disable(DMA_SPI_PORT, DMA_SPI_RX_CHANNEL);
spi_dma_enable(SPI1, SPI_DMA_RECEIVE);
}
void dma_tx_init(void) {
dma_single_data_parameter_struct dma_tx_init_struct;
// 启用DMA0通道3(SPI1_TX的默认通道)
rcu_periph_clock_enable(RCU_DMA0);
dma_deinit(DMA0, DMA_SPI_TX_CHANNEL);
dma_tx_init_struct.direction = DMA_MEMORY_TO_PERIPH;
dma_tx_init_struct.memory0_addr = (uint32_t)spi_tx_buffer; // 发送缓冲区地址
dma_tx_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_tx_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
dma_tx_init_struct.number = 27; // 传输27字节
dma_tx_init_struct.periph_addr = (uint32_t)&SPI_DATA(SPI1); // SPI数据寄存器地址
dma_tx_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_tx_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_tx_init_struct.circular_mode = DMA_CIRCULAR_MODE_DISABLE;
dma_single_data_mode_init(DMA0, DMA_SPI_TX_CHANNEL, &dma_tx_init_struct);
dma_channel_subperipheral_select(DMA0, DMA_SPI_TX_CHANNEL, DMA_SUBPERI0); // 绑定SPI1_TX请求
spi_dma_enable(SPI1, SPI_DMA_TRANSMIT); // 使能SPI发送DMA
}
从机的adc准备好数据之后,发送中断给我们。我们在中断中打开DMA通信,开始接收从机的DMA数据。
// PC4 的 EXTI 中断服务函数
void EXTI4_IRQHandler(void)
{
if (exti_interrupt_flag_get(EXTI_4) != RESET) // 检查中断标志
{
exti_interrupt_flag_clear(EXTI_4);
dma_interrupt_flag_clear(DMA_SPI_PORT, DMA_SPI_RX_CHANNEL, DMA_INT_FLAG_FTF);
dma_interrupt_flag_clear(DMA_SPI_PORT, DMA_SPI_TX_CHANNEL, DMA_INT_FLAG_FTF);
volatile uint8_t dummy = SPI_DATA(SPI1); // 清空DMA通道中的数据
(void)dummy; // 避免编译器警告
// 重置传输数量
dma_memory_address_config(DMA0, DMA_SPI_RX_CHANNEL,DMA_MEMORY_0, (uint32_t)ADC_2[adc_cnt]);
dma_transfer_number_config(DMA0, DMA_SPI_TX_CHANNEL, 27);
dma_transfer_number_config(DMA0, DMA_SPI_RX_CHANNEL, 27);
// 先启动接收DMA,再启动发送DMA(避免数据覆盖)
dma_channel_enable(DMA0, DMA_SPI_TX_CHANNEL); // 发送
dma_channel_enable(DMA0, DMA_SPI_RX_CHANNEL); // 接收
}
}
void DMA0_Channel3_IRQHandler(void) {
if (dma_interrupt_flag_get(DMA_SPI_PORT, DMA_SPI_RX_CHANNEL, DMA_FLAG_FTF)) { // 检查传输完成标志
// 先清除中断标志
dma_interrupt_flag_clear(DMA_SPI_PORT, DMA_SPI_RX_CHANNEL, DMA_FLAG_FTF);
dma_interrupt_flag_clear(DMA_SPI_PORT, DMA_SPI_TX_CHANNEL, DMA_FLAG_FTF);
if(((ADC_2[adc_cnt][0]&0xC0)==0xC0)){
adc_cnt++;
}
}
}
问题记录
1. 修改晶振频率
gd32f450系统自带的晶振频率是25MHz,但程序里默认使用的是16MHz的内部晶振,如果想修改为外部晶振,需要修改下面三处地方。
① 修改 gd32f4xx.h文件中关于外部晶振频率的设置
如果是使用25MHz的则不需要修改。
② 修改 system_gd32f4xx.c 文件中的两处地方
第一处是开头的系统主晶振频率,将其修改为外部晶振。
第二处是选择我们想用的外部晶振对应的频率宏定义即可
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_45113070/article/details/145896731
|
|