[经验分享] 【GD32F350 LogicKids】基本外设驱动

ljj3166   2018-9-7 16:15 楼主
撸完PCB后
花了几天时间,调试了一下基本的片上外设
1、GPIO
输出:
  1. #define MCU_LED_PORT GPIOC
  2. #define USR_LED_PORT GPIOA
  3. #define MCU_LED_PIN GPIO_PIN_13
  4. #define USR_LED_PIN GPIO_PIN_15
  5. #define MCU_LED_RCU RCU_GPIOC
  6. #define USR_LED_RCU RCU_GPIOA
  7. #define USR_LED_ON GPIO_BC(USR_LED_PORT) = USR_LED_PIN
  8. #define USR_LED_OFF GPIO_BOP(USR_LED_PORT) = USR_LED_PIN
  9. #define USR_LED_TG GPIO_TG(USR_LED_PORT) = USR_LED_PIN
  10. #define MCU_LED_ON GPIO_BC(MCU_LED_PORT) = MCU_LED_PIN
  11. #define MCU_LED_OFF GPIO_BOP(MCU_LED_PORT) = MCU_LED_PIN
  12. #define MCU_LED_TG GPIO_TG(MCU_LED_PORT) = MCU_LED_PIN
  13. /* enable the led clock */
  14. rcu_periph_clock_enable(MCU_LED_RCU);
  15. rcu_periph_clock_enable(USR_LED_RCU);
  16. /* configure mcu_led GPIO port */
  17. gpio_mode_set(MCU_LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, MCU_LED_PIN);
  18. gpio_output_options_set(MCU_LED_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, MCU_LED_PIN);
  19. GPIO_BOP(MCU_LED_PORT) = MCU_LED_PIN;
  20. /* configure user_led GPIO port */
  21. gpio_mode_set(USR_LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, USR_LED_PIN);
  22. gpio_output_options_set(USR_LED_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, USR_LED_PIN);
  23. GPIO_BOP(USR_LED_PORT) = USR_LED_PIN; //拉高
  24. GPIO_BC(USR_LED_PORT) = USR_LED_PIN;//拉低


输入:
  1. #define SYS_FACTORYRESET_PORT GPIOB
  2. #define SYS_WA_SEL_PORT GPIOA
  3. #define SYS_FACTORYRESET_PIN GPIO_PIN_9
  4. #define SYS_WA_SEL_PIN GPIO_PIN_0
  5. #define SYS_FACTORYRESET_RCU RCU_GPIOB
  6. #define SYS_WA_SEL_RCU RCU_GPIOA
  7. rcu_periph_clock_enable(SYS_FACTORYRESET_RCU);
  8. rcu_periph_clock_enable(SYS_WA_SEL_RCU);
  9. gpio_mode_set(SYS_FACTORYRESET_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, SYS_FACTORYRESET_PIN);
  10. gpio_mode_set(SYS_WA_SEL_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, SYS_WA_SEL_PIN);


然后gpio_input_bit_get(SYS_FACTORYRESET_PORT, SYS_FACTORYRESET_PIN);
就能读pin的电平鸟

基本上比较好理解
时钟—端口—模式,很常规的配置过程
比较好的是,官方demo中,每句关键代码、每个函数都会有对应的注释
虽然是国际接轨文
也算是比较贴心了

2、usart

  1. /* enable COM GPIO clock */
  2. rcu_periph_clock_enable(RCU_GPIOA);
  3. /* enable USART clock */
  4. rcu_periph_clock_enable(RCU_USART0);
  5. /* connect port to USARTx_Tx */
  6. gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_9);
  7. /* connect port to USARTx_Rx */
  8. gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_10);
  9. /* configure USART Tx as alternate function push-pull */
  10. gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
  11. gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_10);
  12. /* configure USART Rx as alternate function push-pull */
  13. gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
  14. gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_10);
  15. /* USART configure */
  16. usart_deinit(USART0);
  17. usart_baudrate_set(USART0, 115200U);
  18. usart_receive_config(USART0, USART_RECEIVE_ENABLE);
  19. usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
  20. usart_enable(USART0);


没有采用填充结构体的方式
而是很简单粗暴地直接通过调用各种函数来配置
也是时钟—端口—复用功能这样的配置套路
在实际使用中,没有出现发送时,类似STM32首个字符丢失的问题
估计GD对Usart外设逻辑有一定的优化
发送和接受分别是
  1. usart_data_transmit(uint32_t usart_periph, uint32_t data)
  2. usart_data_receive(uint32_t usart_periph)



如果要发送字符串,就得需要手写一个发送函数了
这里直接重定向,使用printf

  1. /* retarget the C library debug function to the USART */
  2. int fputc(int ch, FILE *f)
  3. {
  4. usart_data_transmit(USART0, (uint8_t)ch);
  5. while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
  6. return ch;
  7. }




3、SPI
GD32F350CBT6支持2路SPI
分别是SPI0和SPI1


  1. rcu_periph_clock_enable(RCU_GPIOA);
  2. rcu_periph_clock_enable(RCU_SPI0);
  3. gpio_af_set(GPIOA, GPIO_AF_0, GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);
  4. gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);
  5. /* SPI0 parameter config */
  6. spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
  7. spi_init_struct.device_mode = SPI_MASTER;
  8. spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT;
  9. spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;
  10. spi_init_struct.nss = SPI_NSS_SOFT;
  11. spi_init_struct.prescale = SPI_PSC_8;
  12. spi_init_struct.endian = SPI_ENDIAN_MSB;
  13. spi_init(spi_periph, &spi_init_struct);
  14. spi_enable(spi_periph);




呃,开始填充结构体了
其中SPI1是可以支持QSPI
并且官方外设库中也放出了相关的配置代码
B10和B11可以用作IO2和IO3


  1. gpio_af_set(GPIOB, GPIO_AF_6, GPIO_PIN_10 | GPIO_PIN_11);
  2. qspi_io23_output_enable(SPI1);
  3. qspi_read_enable(SPI1);
  4. qspi_write_enable(SPI1);
  5. qspi_enable(SPI1);


但是楼主配置后并没有成功
似乎和板载的GD25Q16有关
貌似需要对其进行配置后再进行QSPI的通信
相对比较麻烦
度娘了一下,很多网友反馈QSPI的实际性能受到FLASH性能的影响
读写速度提高有限
最后还是在使用普通的三线SPI
发送一字节


  1. uint8_t SPI_SendByte(uint32_t spi_periph, uint8_t byte)
  2. {
  3. /* Loop while DR register in not emplty */
  4. while (spi_i2s_flag_get(spi_periph, SPI_FLAG_TBE) == RESET);
  5. /* Send byte through the SPI1 peripheral */
  6. spi_i2s_data_transmit(spi_periph, byte);
  7. /* Wait to receive a byte */
  8. while (spi_i2s_flag_get(spi_periph, SPI_FLAG_RBNE) == RESET);
  9. /* Return the byte read from the SPI bus */
  10. return spi_i2s_data_receive(spi_periph);
  11. }




接收一字节


  1. #define Dummy_Byte 0xFF
  2. uint8_t SPI_ReadByte(uint32_t spi_periph)
  3. {
  4. return (SPI_SendByte(spi_periph, Dummy_Byte));
  5. }




4、IIC
有坑
特别是多字节连续读取的时候
官方文档给出了A、B两种读取方式
1.jpg
2.jpg
GD32F350的IIC在每次接收一个字节后
都会硬件产生一个ACK信号
所以在接收多个字节的时候要记得把ACK信号给Disable掉
配置代码

  1. rcu_periph_clock_enable(RCU_GPIOB);
  2. rcu_periph_clock_enable(RCU_I2C0);
  3. gpio_af_set(GPIOB, GPIO_AF_1, GPIO_PIN_6);
  4. gpio_af_set(GPIOB, GPIO_AF_1, GPIO_PIN_7);
  5. gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP,GPIO_PIN_6);
  6. gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ,GPIO_PIN_6);
  7. gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP,GPIO_PIN_7);
  8. gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ,GPIO_PIN_7);
  9. i2c_clock_config(I2C0, 400000, I2C_DTCY_2);
  10. i2c_mode_addr_config(I2C0, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, I2C0_OWN_ADDRESS7);
  11. i2c_enable(i2c_periph);
  12. i2c_ack_config(i2c_periph, I2C_ACK_ENABLE);




发送一个字节


  1. void IIC_SendByte(uint32_t i2c_periph, uint32_t slave_addr, uint32_t sub_addr, uint8_t data)
  2. {
  3. while(i2c_flag_get(i2c_periph, I2C_FLAG_I2CBSY));
  4. i2c_start_on_bus(i2c_periph);
  5. while(!i2c_flag_get(i2c_periph, I2C_FLAG_SBSEND));
  6. i2c_master_addressing(i2c_periph, slave_addr, I2C_TRANSMITTER);
  7. while(!i2c_flag_get(i2c_periph, I2C_FLAG_ADDSEND));
  8. i2c_flag_clear(i2c_periph, I2C_FLAG_ADDSEND);
  9. while(!i2c_flag_get(i2c_periph, I2C_FLAG_TBE));
  10. i2c_data_transmit(i2c_periph, sub_addr);
  11. while(!i2c_flag_get(i2c_periph, I2C_FLAG_BTC));
  12. i2c_data_transmit(i2c_periph, data);
  13. while(!i2c_flag_get(i2c_periph, I2C_FLAG_BTC));
  14. i2c_stop_on_bus(i2c_periph);
  15. while(I2C_CTL0(i2c_periph)&0x0200);
  16. }




接收一个字节


  1. uint8_t IIC_ReadByte(uint32_t i2c_periph, uint32_t slave_addr, uint32_t sub_addr)
  2. {
  3. uint8_t i2c_receiver;
  4. while(i2c_flag_get(i2c_periph, I2C_FLAG_I2CBSY));
  5. i2c_start_on_bus(i2c_periph);
  6. while(!i2c_flag_get(i2c_periph, I2C_FLAG_SBSEND));
  7. i2c_master_addressing(i2c_periph, slave_addr, I2C_TRANSMITTER);
  8. while(!i2c_flag_get(i2c_periph, I2C_FLAG_ADDSEND));
  9. i2c_flag_clear(i2c_periph, I2C_FLAG_ADDSEND);
  10. i2c_data_transmit(i2c_periph, sub_addr);
  11. while(!i2c_flag_get(i2c_periph, I2C_FLAG_BTC));
  12. i2c_start_on_bus(i2c_periph);
  13. while(!i2c_flag_get(i2c_periph, I2C_FLAG_SBSEND));
  14. i2c_master_addressing(i2c_periph, slave_addr, I2C_RECEIVER);
  15. while(!i2c_flag_get(i2c_periph, I2C_FLAG_ADDSEND));
  16. i2c_flag_clear(i2c_periph, I2C_FLAG_ADDSEND);
  17. i2c_ack_config(i2c_periph, I2C_ACK_DISABLE);
  18. while(!i2c_flag_get(i2c_periph, I2C_FLAG_RBNE));
  19. i2c_receiver = i2c_data_receive(i2c_periph);
  20. i2c_stop_on_bus(i2c_periph);
  21. while(I2C_CTL0(i2c_periph)&0x0200);
  22. i2c_ack_config(i2c_periph, I2C_ACK_ENABLE);
  23. return i2c_receiver;
  24. }




发送一堆字节


  1. void IIC_SendBuffer(uint32_t i2c_periph, uint32_t slave_addr, uint32_t sub_addr, uint8_t *buffer, uint16_t send_nums)
  2. {
  3. uint16_t i=0;
  4. while(i2c_flag_get(i2c_periph, I2C_FLAG_I2CBSY));
  5. i2c_start_on_bus(i2c_periph);
  6. while(!i2c_flag_get(i2c_periph, I2C_FLAG_SBSEND));
  7. i2c_master_addressing(i2c_periph, slave_addr, I2C_TRANSMITTER);
  8. while(!i2c_flag_get(i2c_periph, I2C_FLAG_ADDSEND));
  9. i2c_flag_clear(i2c_periph, I2C_FLAG_ADDSEND);
  10. while(!i2c_flag_get(i2c_periph, I2C_FLAG_TBE));
  11. i2c_data_transmit(i2c_periph, sub_addr);
  12. while(!i2c_flag_get(i2c_periph, I2C_FLAG_BTC));
  13. for(i=0; i<send_nums; i++)
  14. {
  15. i2c_data_transmit(i2c_periph, buffer[i]);
  16. while(!i2c_flag_get(i2c_periph, I2C_FLAG_BTC));
  17. }
  18. i2c_stop_on_bus(i2c_periph);
  19. while(I2C_CTL0(i2c_periph)&0x0200);
  20. }




接收一堆字节


  1. void IIC_ReadBuffer(uint32_t i2c_periph, uint32_t slave_addr, uint32_t sub_addr, uint8_t *buffer, uint16_t read_nums)
  2. {
  3. uint16_t cnt = 0;
  4. while(i2c_flag_get(i2c_periph, I2C_FLAG_I2CBSY));
  5. i2c_start_on_bus(i2c_periph);
  6. while(!i2c_flag_get(i2c_periph, I2C_FLAG_SBSEND));
  7. i2c_master_addressing(i2c_periph, slave_addr, I2C_TRANSMITTER);
  8. while(!i2c_flag_get(i2c_periph, I2C_FLAG_ADDSEND));
  9. i2c_flag_clear(i2c_periph, I2C_FLAG_ADDSEND);
  10. i2c_data_transmit(i2c_periph, sub_addr);
  11. while(!i2c_flag_get(i2c_periph, I2C_FLAG_BTC));
  12. i2c_start_on_bus(i2c_periph);
  13. while(!i2c_flag_get(i2c_periph, I2C_FLAG_SBSEND));
  14. i2c_master_addressing(i2c_periph, slave_addr, I2C_RECEIVER);
  15. while(!i2c_flag_get(i2c_periph, I2C_FLAG_ADDSEND));
  16. i2c_flag_clear(i2c_periph, I2C_FLAG_ADDSEND);

  17. if(read_nums > 2)
  18. {
  19. for(cnt = 0; cnt < read_nums; cnt++)
  20. {
  21. if((read_nums > 1) && (cnt == read_nums-1))
  22. {
  23. while(!i2c_flag_get(I2C0, I2C_FLAG_BTC));
  24. i2c_ack_config(I2C0, I2C_ACK_DISABLE);
  25. }
  26. while(!i2c_flag_get(i2c_periph, I2C_FLAG_RBNE));
  27. buffer[cnt] = i2c_data_receive(i2c_periph);
  28. }
  29. }
  30. else
  31. {
  32. for(cnt = 0; cnt < read_nums; cnt++)
  33. {
  34. while(!i2c_flag_get(i2c_periph, I2C_FLAG_RBNE));
  35. buffer[cnt] = i2c_data_receive(i2c_periph);
  36. i2c_ack_config(i2c_periph, I2C_ACK_DISABLE);
  37. }
  38. }
  39. i2c_stop_on_bus(i2c_periph);
  40. while(I2C_CTL0(i2c_periph)&0x0200);
  41. i2c_ack_config(i2c_periph, I2C_ACK_ENABLE);
  42. }




5、PWM
抄库


  1. void PWM_INIT(void)
  2. {
  3. timer_oc_parameter_struct timer_ocintpara;
  4. timer_parameter_struct timer_initpara;

  5. /*Configure PB3 PB4 PB5(TIMER1-CH1 TIMER2-CH0 TIMER2-CH1) as alternate function*/
  6. gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
  7. gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_3);

  8. gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4);
  9. gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_4);

  10. gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
  11. gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_5);

  12. gpio_af_set(GPIOB, GPIO_AF_2, GPIO_PIN_3);
  13. gpio_af_set(GPIOB, GPIO_AF_1, GPIO_PIN_4);
  14. gpio_af_set(GPIOB, GPIO_AF_1, GPIO_PIN_5);

  15. rcu_periph_clock_enable(RCU_TIMER1);
  16. rcu_periph_clock_enable(RCU_TIMER2);
  17. timer_deinit(TIMER1);
  18. timer_deinit(TIMER2);
  19. /* TIMER1 configuration */
  20. timer_initpara.prescaler = 107;
  21. timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
  22. timer_initpara.counterdirection = TIMER_COUNTER_UP;
  23. timer_initpara.period = 15999;
  24. timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
  25. timer_initpara.repetitioncounter = 0;
  26. timer_init(TIMER1,&timer_initpara);
  27. /* TIMER2 configuration */
  28. timer_initpara.prescaler = 107;
  29. timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
  30. timer_initpara.counterdirection = TIMER_COUNTER_UP;
  31. timer_initpara.period = 15999;
  32. timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
  33. timer_initpara.repetitioncounter = 0;
  34. timer_init(TIMER2,&timer_initpara);

  35. /*configuration in PWM mode0 */
  36. timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
  37. timer_ocintpara.outputstate = TIMER_CCX_ENABLE;
  38. timer_channel_output_pulse_value_config(TIMER1,TIMER_CH_1,15999);
  39. timer_channel_output_mode_config(TIMER1,TIMER_CH_1,TIMER_OC_MODE_PWM0);
  40. timer_channel_output_shadow_config(TIMER1,TIMER_CH_1,TIMER_OC_SHADOW_DISABLE);

  41. timer_channel_output_pulse_value_config(TIMER2,TIMER_CH_0,15999);
  42. timer_channel_output_mode_config(TIMER2,TIMER_CH_0,TIMER_OC_MODE_PWM0);
  43. timer_channel_output_shadow_config(TIMER2,TIMER_CH_0,TIMER_OC_SHADOW_DISABLE);

  44. timer_channel_output_pulse_value_config(TIMER2,TIMER_CH_1,15999);
  45. timer_channel_output_mode_config(TIMER2,TIMER_CH_1,TIMER_OC_MODE_PWM0);
  46. timer_channel_output_shadow_config(TIMER2,TIMER_CH_1,TIMER_OC_SHADOW_DISABLE);
  47. /* auto-reload preload enable */
  48. timer_auto_reload_shadow_enable(TIMER1);
  49. timer_auto_reload_shadow_enable(TIMER2);
  50. /* auto-reload preload enable */
  51. timer_enable(TIMER1);
  52. timer_enable(TIMER2);
  53. timer_channel_output_config(TIMER1,TIMER_CH_1,&timer_ocintpara);
  54. timer_channel_output_config(TIMER2,TIMER_CH_0,&timer_ocintpara);
  55. timer_channel_output_config(TIMER2,TIMER_CH_1,&timer_ocintpara);
  56. }




修改了几个端口和TimerX
重载timer_channel_output_pulse_value_config 中15999的值就能调整占空比了


上个测试视频


需要使用的外设基本上就是这么多了
先到这



So TM what......?

回复评论 (8)

我在长沙发现了楼主
顺道消灭没有人回复
So TM what......?
点赞  2018-9-7 16:16
楼主写的非常好留个标记,后边就可以做个参考少入坑
点赞  2018-9-9 18:04
嗯,收藏了
点赞  2018-9-13 23:13
顶一个
点赞  2018-9-18 11:47
赞一个
点赞  2018-9-19 12:17
花了几天时间调串口,还是无法接收电脑的信息,楼主的资料正好可以参考,谢谢!
点赞  2018-9-20 08:06

很详细的资料,Thank you!

点赞  2022-8-8 20:13

非常详细,谢谢分享

点赞  2022-12-3 11:50
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复