[其他] 【HC32L196PCTA测评】4.UART通讯+SPI驱动12864LCD+I2C测试

[复制链接]
 楼主| 发表于 2023-8-7 10:56 | 显示全部楼层 |阅读模式
本帖最后由 yuyy1989 于 2023-8-11 22:39 编辑

#申请原创# @21小跑堂  
4.UART通讯+SPI驱动12864LCD+I2C测试
4.1UART通讯测试
UART(Universal Asynchronous Receiver/Transmitter)通用异步收发器,在嵌入式领域应用的非常广泛,选用PB08和PB09作为UART0的TX和RX
QQ截图20230806144720.png
初始化IO
  1. void uart0_io_init()
  2. {
  3.     stc_gpio_cfg_t stcGpioCfg;
  4.     DDL_ZERO_STRUCT(stcGpioCfg);
  5.     Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE); //使能GPIO模块时钟

  6.     stcGpioCfg.enDir = GpioDirOut;
  7.     Gpio_Init(GpioPortB, GpioPin8, &stcGpioCfg);
  8.     Gpio_SetAfMode(GpioPortB, GpioPin8, GpioAf7);

  9.     stcGpioCfg.enDir = GpioDirIn;
  10.     Gpio_Init(GpioPortB, GpioPin9, &stcGpioCfg);
  11.     Gpio_SetAfMode(GpioPortB, GpioPin9, GpioAf7);
  12. }
初始化串口参数,波特率115200
  1. void uart0_init()
  2. {
  3.     stc_uart_cfg_t    stcCfg;
  4.     DDL_ZERO_STRUCT(stcCfg);
  5.     ///< 开启外设时钟
  6.     Sysctrl_SetPeripheralGate(SysctrlPeripheralUart0,TRUE);///<使能uart0模块时钟
  7.     ///<UART Init
  8.     stcCfg.enRunMode        = UartMskMode1;          ///<模式1
  9.     stcCfg.enStopBit        = UartMsk1bit;           ///<1bit停止位
  10.     stcCfg.stcBaud.u32Baud  = 115200;                ///<115200
  11.     stcCfg.stcBaud.enClkDiv = UartMsk8Or16Div;       ///<通道采样分频配置
  12.     stcCfg.stcBaud.u32Pclk  = Sysctrl_GetPClkFreq(); ///<获得外设时钟(PCLK)频率值
  13.     Uart_Init(M0P_UART0, &stcCfg);                   ///<串口初始化
  14. }
先实现一下Printf重定向到串口,需要在工程配置里勾选Use MicroLIB并包含stdio.h
QQ截图20230807103351.png
重写fputc方法
  1. int fputc(int ch, FILE* f)
  2. {
  3.     Uart_SendDataPoll(M0P_UART0,ch);
  4.     return (ch);
  5. }
在main函数内每隔1秒打印测试下
  1. int32_t main(void)
  2. {
  3.     uint16_t test = 0;
  4.     xth_init();
  5.     //时钟分频设置
  6.     Sysctrl_SetHCLKDiv(SysctrlHclkDiv1);
  7.     Sysctrl_SetPCLKDiv(SysctrlPclkDiv1);
  8.    
  9.     uart0_io_init();
  10.     uart0_init();

  11.     while(1)
  12.     {
  13.         printf("HC32L196PCTA 串口printf测试 %d",test);
  14.         test += 1;
  15.         delay1ms(1000);
  16.     }
  17. }
连接PB08与DapLink的串口RX,使用串口调试工具查看运行效果
QQ截图20230806151427.png
接下来开启中断,将接收到的数据原样返回,查看可以使用的串口中断控制
QQ截图20230806152452.png
发现有接收中断和发送中断,但是没有空闲中断,利用之前用过的定时器实现串口空闲判定,先开启接收中断
  1. void uart0_init()
  2. {
  3.     stc_uart_cfg_t    stcCfg;
  4.     DDL_ZERO_STRUCT(stcCfg);
  5.     ///< 开启外设时钟
  6.     Sysctrl_SetPeripheralGate(SysctrlPeripheralUart0,TRUE);///<使能uart0模块时钟
  7.     ///<UART Init
  8.     stcCfg.enRunMode        = UartMskMode1;          ///<模式1
  9.     stcCfg.enStopBit        = UartMsk1bit;           ///<1bit停止位
  10.     stcCfg.stcBaud.u32Baud  = 115200;                ///<115200
  11.     stcCfg.stcBaud.enClkDiv = UartMsk8Or16Div;       ///<通道采样分频配置
  12.     stcCfg.stcBaud.u32Pclk  = Sysctrl_GetPClkFreq(); ///<获得外设时钟(PCLK)频率值
  13.     Uart_Init(M0P_UART0, &stcCfg);                   ///<串口初始化
  14.    
  15.     Uart_ClrStatus(M0P_UART0,UartRC);           //清接收请求
  16.     Uart_EnableIrq(M0P_UART0,UartRxIrq);        //使能串口接收中断
  17.     EnableNvic(UART0_2_IRQn, IrqLevel3, TRUE);  ///<系统中断使能
  18. }
  1. void timer0_init()
  2. {
  3.     stc_bt_mode0_cfg_t     stcBtBaseCfg;
  4.     DDL_ZERO_STRUCT(stcBtBaseCfg);
  5.     Sysctrl_SetPeripheralGate(SysctrlPeripheralBaseTim, TRUE); //Base Timer外设时钟使能
  6.    
  7.     stcBtBaseCfg.enWorkMode = BtWorkMode0;                  //定时器模式
  8.     stcBtBaseCfg.enCT       = BtTimer;                      //定时器功能,计数时钟为内部PCLK
  9.     stcBtBaseCfg.enPRS      = BtPCLKDiv32;                  //PCLK/32 32M/32=1M
  10.     stcBtBaseCfg.enCntMode  = Bt16bitArrMode;               //自动重载16位计数器/定时器
  11.     stcBtBaseCfg.bEnTog     = FALSE;
  12.     stcBtBaseCfg.bEnGate    = FALSE;
  13.     Bt_Mode0_Init(TIM0, &stcBtBaseCfg);                     //TIM0 的模式0功能初始化
  14.    
  15.     Bt_M0_ARRSet(TIM0, 0x10000-1000);                       //设置重载值
  16.     Bt_M0_Cnt16Set(TIM0, 0x10000-1000);                     //设置计数初值
  17.    
  18.     Bt_ClearIntFlag(TIM0,BtUevIrq);                         //清中断标志   
  19.     Bt_Mode0_EnableIrq(TIM0);                               //使能TIM0中断
  20.     EnableNvic(TIM0_IRQn, IrqLevel3, TRUE);                 //TIM0中断使能
  21.     Bt_M0_Run(TIM0);                                        //TIM0 运行。
  22. }
串口中断函数处理,当接收达到一定长度时开始发送,每次接收重置超时计数
  1. #define UART_BUFFER_LEN 30
  2. uint8_t uart_buffer[UART_BUFFER_LEN] = {0};
  3. uint8_t uart_rxindex = 0;
  4. uint8_t uart_rxlen = 0;
  5. uint8_t uart_txindex = 0;
  6. uint8_t uart_txlen = 0;
  7. uint8_t uartrx_timeout = 0;
  8. void uart_starttx()
  9. {
  10.     if(uart_rxlen > 0)
  11.     {
  12.         uart_txlen += uart_rxlen;
  13.         uart_rxlen = 0;
  14.         Uart_EnableIrq(M0P_UART0,UartTxEIrq);
  15.     }
  16. }
  17. void Uart0_IRQHandler(void)
  18. {
  19.     if(Uart_GetStatus(M0P_UART0, UartRC))         //UART0数据接收
  20.     {
  21.         uartrx_timeout = 5;
  22.         uart_buffer[uart_rxindex++] = Uart_ReceiveData(M0P_UART0);
  23.         if(uart_rxindex == UART_BUFFER_LEN)
  24.             uart_rxindex = 0;
  25.         uart_rxlen++;
  26.         Uart_ClrStatus(M0P_UART0, UartRC);        //清中断状态位
  27.     }
  28.     if(uart_rxlen > 15)
  29.         uart_starttx();
  30.     if(Uart_GetStatus(M0P_UART0, UartTxe))         //UART0数据发送
  31.     {
  32.         if(uart_txlen > 0)
  33.         {
  34.             Uart_SendDataIt(M0P_UART0, uart_buffer[uart_txindex++]);
  35.             if(uart_txindex == UART_BUFFER_LEN)
  36.                 uart_txindex = 0;
  37.             uart_txlen--;
  38.         }
  39.         else
  40.         {
  41.             Uart_DisableIrq(M0P_UART0,UartTxEIrq);
  42.         }
  43.         Uart_ClrStatus(M0P_UART0, UartTxe);        //清中断状态位
  44.     }
  45. }
在定时中断中超时计数递减,当减到0后发送剩余的数据
  1. void Tim0_IRQHandler(void)
  2. {
  3.     //Timer0 模式0 溢出中断
  4.     if(TRUE == Bt_GetIntFlag(TIM0, BtUevIrq))
  5.     {
  6.         if(uartrx_timeout > 0)
  7.         {
  8.             uartrx_timeout -= 1;
  9.             if(uartrx_timeout == 0)
  10.                 uart_starttx();
  11.         }
  12.         Bt_ClearIntFlag(TIM0,BtUevIrq); //中断标志清零
  13.     }
  14. }
运行效果
QQ截图20230806195017.png

4.2SPI通讯测试
SPI(Serial Peripheral Interface)串行外设接口是一种高速的全双工同步的通信总线,需要至少SCK MOSI MISO三根线实现数据双向传输,可以通过片选线来实现对不同设备的访问
手里有个12864的LCD屏是SPI接口的拿来测试一下,通讯需要用到5个IO:SCK MOSI CS RST A0都是输出模式,板子已经定义好了2组SPI的IO
QQ截图20230806200745.png
这里直接沿用,初始化SPI0的IO,这里只初始化SCK MOSI,CS由软件控制输出

  1. void spi_gpio_init()
  2. {
  3.     stc_gpio_cfg_t GpioInitStruct;
  4.     DDL_ZERO_STRUCT(GpioInitStruct);
  5.    
  6.     Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE);
  7.    
  8.     ///< SPI0引脚配置:主机
  9.     GpioInitStruct.enDrv = GpioDrvH;
  10.     GpioInitStruct.enDir = GpioDirOut;   
  11.                                                                
  12.     Gpio_Init(STK_SPI0_SCK_PORT, STK_SPI0_SCK_PIN, &GpioInitStruct);            
  13.     Gpio_SetAfMode(STK_SPI0_SCK_PORT, STK_SPI0_SCK_PIN, GpioAf2);           ///<配置SPI0_SCK                                                              
  14.     Gpio_Init(STK_SPI0_MOSI_PORT, STK_SPI0_MOSI_PIN, &GpioInitStruct);           
  15.     Gpio_SetAfMode(STK_SPI0_MOSI_PORT, STK_SPI0_MOSI_PIN, GpioAf2);         ///<配置SPI0_MOSI                                                         
  16. }
初始化SPI
  1. void spi_init()
  2. {
  3.     stc_spi_cfg_t  SpiInitStruct;   
  4.     Sysctrl_SetPeripheralGate(SysctrlPeripheralSpi0,TRUE);
  5.     Reset_RstPeripheral0(ResetMskSpi0);
  6.     //SPI0模块配置:主机
  7.     SpiInitStruct.enSpiMode = SpiMskMaster;     //配置位主机模式
  8.     SpiInitStruct.enPclkDiv = SpiClkMskDiv4;   
  9.     SpiInitStruct.enCPHA    = SpiMskCphasecond; //第2边沿采样
  10.     SpiInitStruct.enCPOL    = SpiMskcpolhigh;   //极性为高
  11.     Spi_Init(M0P_SPI0, &SpiInitStruct);
  12. }
初始化LCD,需要注意的是虽然是用的软件控制CS引脚但是在使用SPI通讯时也先要调用Spi_SetCS设置CS
  1. YUYY_HS12864G18B_DEV_t hs12864_ctr;
  2. void hs12864_init()
  3. {
  4.     stc_gpio_cfg_t stcGpioCfg;
  5.     Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
  6.     stcGpioCfg.enDir = GpioDirOut;
  7.     stcGpioCfg.enPu = GpioPuDisable;
  8.     stcGpioCfg.enPd = GpioPdEnable;
  9.     Gpio_ClrIO(GpioPortE, GpioPin11);
  10.     Gpio_Init(GpioPortE, GpioPin11, &stcGpioCfg);
  11.     Gpio_ClrIO(GpioPortE, GpioPin12);
  12.     Gpio_Init(GpioPortE, GpioPin12, &stcGpioCfg);
  13.     Gpio_ClrIO(GpioPortE, GpioPin14);
  14.     Gpio_Init(GpioPortE, GpioPin14, &stcGpioCfg);
  15.     hs12864_ctr.spix = M0P_SPI0;
  16.     hs12864_ctr.a0.gpio = GpioPortE;
  17.     hs12864_ctr.a0.pin = GpioPin11;
  18.     hs12864_ctr.cs.gpio = GpioPortE;
  19.     hs12864_ctr.cs.pin = GpioPin12;
  20.     hs12864_ctr.rst.gpio = GpioPortE;
  21.     hs12864_ctr.rst.pin = GpioPin14;
  22.     Spi_SetCS(M0P_SPI0, FALSE);
  23.     yuyy_hs12864g18b_init(&hs12864_ctr);
  24.     yuyy_hs12864g18b_clear_screen(&hs12864_ctr);
  25.     yuyy_hs12864g18b_display_string_8x16(&hs12864_ctr,0,0,0,(uint8_t *)"HC32L196PCTA");
  26.     yuyy_hs12864g18b_display_string_8x16(&hs12864_ctr,0,2,0,(uint8_t *)"SPI LCD Test");
  27.     yuyy_hs12864g18b_display_string_8x16(&hs12864_ctr,0,4,0,(uint8_t *)"Code by yuyy1989");
  28.     yuyy_hs12864g18b_display_finish(&hs12864_ctr);
  29.     Spi_SetCS(M0P_SPI0, TRUE);
  30. }
运行效果
微信图片_20230806223047.jpg

4.3I2C测试读取DHTC12温湿度
I2C(Inter-Integrated Circuit)是双线双向的同步串行总线,它利用一根时钟线和一根数据线在连接总线的两个器件之间进行信息的传递,为设备之间数据交换提供了一种简单高效的方法
板子定义好了一组I2C的IO
QQ截图20230806222710.png
不过这里的PB06参与了别的电路,实测用这组IO也不能完成通讯
QQ截图20230807100011.png
换用PB10 PB11
QQ截图20230807104924.png
初始化IO


  1. void i2c_io_init()
  2. {
  3.     stc_gpio_cfg_t stcGpioCfg;
  4.     DDL_ZERO_STRUCT(stcGpioCfg);
  5.     Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE);
  6.    
  7.     stcGpioCfg.enDir = GpioDirOut;                           ///< 端口方向配置->输出   
  8.     stcGpioCfg.enOD = GpioOdEnable;                          ///< 开漏输出
  9.     stcGpioCfg.enPu = GpioPuEnable;                          ///< 端口上拉配置->使能
  10.     stcGpioCfg.enPd = GpioPdDisable;                         ///< 端口下拉配置->禁止
  11.     Gpio_Init(GpioPortB,GpioPin10,&stcGpioCfg);               ///< 端口初始化
  12.     Gpio_Init(GpioPortB,GpioPin11,&stcGpioCfg);
  13.     Gpio_SetAfMode(GpioPortB,GpioPin10,GpioAf1);              ///< 配置PB10为SCL
  14.     Gpio_SetAfMode(GpioPortB,GpioPin11,GpioAf1);              ///< 配置PB11为SDA            
  15. }
初始化I2C
  1. void i2c_init()
  2. {
  3.     stc_i2c_cfg_t stcI2cCfg;
  4.     DDL_ZERO_STRUCT(stcI2cCfg);                           
  5.     Sysctrl_SetPeripheralGate(SysctrlPeripheralI2c1,TRUE);
  6.     stcI2cCfg.u32Pclk = Sysctrl_GetPClkFreq();             ///< 获取PCLK时钟
  7.     stcI2cCfg.u32Baud = 100000;                            ///< 100KHz
  8.     stcI2cCfg.enMode = I2cMasterMode;                      ///< 主机模式
  9.     stcI2cCfg.bGc = FALSE;                                 ///< 广播地址应答使能关闭
  10.     I2C_Init(M0P_I2C1,&stcI2cCfg);                         ///< 模块初始化
  11. }
根据手册提供的状态码编写收发方法,可以直接参考例程中的方法
QQ截图20230806224922.png
初始化DHTC12

  1. void i2c_dev_init()
  2. {
  3.     iic_dev.iictype = YUYY_IIC_TYPE_HARD;
  4.     iic_dev.hiicx = M0P_I2C1;
  5.     dhtc12_dev.iicx = &iic_dev;
  6.     yuyy_dhtc12_init(&dhtc12_dev);
  7. }
main函数中用12864LCD显示温湿度
  1. int32_t main(void)
  2. {
  3.     float HumF,TemF;
  4.     char out[20];
  5.     uint8_t err=0,outlen;
  6.     xth_init();
  7.     //时钟分频设置
  8.     Sysctrl_SetHCLKDiv(SysctrlHclkDiv1);
  9.     Sysctrl_SetPCLKDiv(SysctrlPclkDiv1);
  10.    
  11.     spi_gpio_init();
  12.     spi_init();
  13.     hs12864_init();
  14.    
  15.     i2c_io_init();
  16.     i2c_init();
  17.     i2c_dev_init();
  18.     while(1)
  19.     {
  20.         Spi_SetCS(M0P_SPI0, FALSE);
  21.         err = yuyy_dhtc12_readHT(&dhtc12_dev,&TemF,&HumF);
  22.         outlen = sprintf(out,"%d %.1f%% %.1f",err,HumF,TemF);
  23.         yuyy_hs12864g18b_display_string_8x16(&hs12864_ctr,0,4,0,(uint8_t *)out);
  24.         yuyy_hs12864g18b_display_graphic_16x16(&hs12864_ctr,0,4,8*outlen,(uint8_t *)test16x16temp);
  25.         yuyy_hs12864g18b_display_string_8x16(&hs12864_ctr,0,4,8*(outlen+2),(uint8_t *)"      ");
  26.         Spi_SetCS(M0P_SPI0, TRUE);
  27.         yuyy_delay_ms(1000);
  28.     }
  29. }


运行效果
微信图片_20230807104117.jpg

发表于 2023-8-7 11:51 来自手机 | 显示全部楼层
12864液晶屏什么时候出SPI接口了
 楼主| 发表于 2023-8-7 15:07 | 显示全部楼层
guijial511 发表于 2023-8-7 11:51
12864液晶屏什么时候出SPI接口了

应该很早就有了吧,这个屏用的IC文档修改记录最早到07年
发表于 2023-8-7 20:59 | 显示全部楼层
guijial511 发表于 2023-8-7 11:51
12864液晶屏什么时候出SPI接口了

小彩屏都有很多SPI的了,这种黑白屏更多,前几天淘了几块可以并口、SPI、IIC口的192x96的屏,挺不错,比并口屏接线简单多了
发表于 2023-10-24 15:44 | 显示全部楼层
请问有源码分享吗
发表于 2025-5-7 14:49 | 显示全部楼层
工程源码可以分享一下吗
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:同飞软件研发工程师
简介:制冷系统单片机软件开发,使用PID控制温度

161

主题

814

帖子

10

粉丝
快速回复 返回顶部 返回列表