打印
[其他]

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

[复制链接]
1122|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 yuyy1989 于 2023-8-11 22:39 编辑

#申请原创# @21小跑堂  
4.UART通讯+SPI驱动12864LCD+I2C测试
4.1UART通讯测试
UART(Universal Asynchronous Receiver/Transmitter)通用异步收发器,在嵌入式领域应用的非常广泛,选用PB08和PB09作为UART0的TX和RX

初始化IO
void uart0_io_init()
{
    stc_gpio_cfg_t stcGpioCfg;
    DDL_ZERO_STRUCT(stcGpioCfg);
    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE); //使能GPIO模块时钟

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

    stcGpioCfg.enDir = GpioDirIn;
    Gpio_Init(GpioPortB, GpioPin9, &stcGpioCfg);
    Gpio_SetAfMode(GpioPortB, GpioPin9, GpioAf7);
}
初始化串口参数,波特率115200
void uart0_init()
{
    stc_uart_cfg_t    stcCfg;
    DDL_ZERO_STRUCT(stcCfg);
    ///< 开启外设时钟
    Sysctrl_SetPeripheralGate(SysctrlPeripheralUart0,TRUE);///<使能uart0模块时钟
    ///<UART Init
    stcCfg.enRunMode        = UartMskMode1;          ///<模式1
    stcCfg.enStopBit        = UartMsk1bit;           ///<1bit停止位
    stcCfg.stcBaud.u32Baud  = 115200;                ///<115200
    stcCfg.stcBaud.enClkDiv = UartMsk8Or16Div;       ///<通道采样分频配置
    stcCfg.stcBaud.u32Pclk  = Sysctrl_GetPClkFreq(); ///<获得外设时钟(PCLK)频率值
    Uart_Init(M0P_UART0, &stcCfg);                   ///<串口初始化
}
先实现一下Printf重定向到串口,需要在工程配置里勾选Use MicroLIB并包含stdio.h

重写fputc方法
int fputc(int ch, FILE* f)
{
    Uart_SendDataPoll(M0P_UART0,ch);
    return (ch);
}
在main函数内每隔1秒打印测试下
int32_t main(void)
{
    uint16_t test = 0;
    xth_init();
    //时钟分频设置
    Sysctrl_SetHCLKDiv(SysctrlHclkDiv1);
    Sysctrl_SetPCLKDiv(SysctrlPclkDiv1);
   
    uart0_io_init();
    uart0_init();

    while(1)
    {
        printf("HC32L196PCTA 串口printf测试 %d",test);
        test += 1;
        delay1ms(1000);
    }
}
连接PB08与DapLink的串口RX,使用串口调试工具查看运行效果

接下来开启中断,将接收到的数据原样返回,查看可以使用的串口中断控制

发现有接收中断和发送中断,但是没有空闲中断,利用之前用过的定时器实现串口空闲判定,先开启接收中断
void uart0_init()
{
    stc_uart_cfg_t    stcCfg;
    DDL_ZERO_STRUCT(stcCfg);
    ///< 开启外设时钟
    Sysctrl_SetPeripheralGate(SysctrlPeripheralUart0,TRUE);///<使能uart0模块时钟
    ///<UART Init
    stcCfg.enRunMode        = UartMskMode1;          ///<模式1
    stcCfg.enStopBit        = UartMsk1bit;           ///<1bit停止位
    stcCfg.stcBaud.u32Baud  = 115200;                ///<115200
    stcCfg.stcBaud.enClkDiv = UartMsk8Or16Div;       ///<通道采样分频配置
    stcCfg.stcBaud.u32Pclk  = Sysctrl_GetPClkFreq(); ///<获得外设时钟(PCLK)频率值
    Uart_Init(M0P_UART0, &stcCfg);                   ///<串口初始化
   
    Uart_ClrStatus(M0P_UART0,UartRC);           //清接收请求
    Uart_EnableIrq(M0P_UART0,UartRxIrq);        //使能串口接收中断
    EnableNvic(UART0_2_IRQn, IrqLevel3, TRUE);  ///<系统中断使能
}
void timer0_init()
{
    stc_bt_mode0_cfg_t     stcBtBaseCfg;
    DDL_ZERO_STRUCT(stcBtBaseCfg);
    Sysctrl_SetPeripheralGate(SysctrlPeripheralBaseTim, TRUE); //Base Timer外设时钟使能
   
    stcBtBaseCfg.enWorkMode = BtWorkMode0;                  //定时器模式
    stcBtBaseCfg.enCT       = BtTimer;                      //定时器功能,计数时钟为内部PCLK
    stcBtBaseCfg.enPRS      = BtPCLKDiv32;                  //PCLK/32 32M/32=1M
    stcBtBaseCfg.enCntMode  = Bt16bitArrMode;               //自动重载16位计数器/定时器
    stcBtBaseCfg.bEnTog     = FALSE;
    stcBtBaseCfg.bEnGate    = FALSE;
    Bt_Mode0_Init(TIM0, &stcBtBaseCfg);                     //TIM0 的模式0功能初始化
   
    Bt_M0_ARRSet(TIM0, 0x10000-1000);                       //设置重载值
    Bt_M0_Cnt16Set(TIM0, 0x10000-1000);                     //设置计数初值
   
    Bt_ClearIntFlag(TIM0,BtUevIrq);                         //清中断标志   
    Bt_Mode0_EnableIrq(TIM0);                               //使能TIM0中断
    EnableNvic(TIM0_IRQn, IrqLevel3, TRUE);                 //TIM0中断使能
    Bt_M0_Run(TIM0);                                        //TIM0 运行。
}
串口中断函数处理,当接收达到一定长度时开始发送,每次接收重置超时计数
#define UART_BUFFER_LEN 30
uint8_t uart_buffer[UART_BUFFER_LEN] = {0};
uint8_t uart_rxindex = 0;
uint8_t uart_rxlen = 0;
uint8_t uart_txindex = 0;
uint8_t uart_txlen = 0;
uint8_t uartrx_timeout = 0;
void uart_starttx()
{
    if(uart_rxlen > 0)
    {
        uart_txlen += uart_rxlen;
        uart_rxlen = 0;
        Uart_EnableIrq(M0P_UART0,UartTxEIrq);
    }
}
void Uart0_IRQHandler(void)
{
    if(Uart_GetStatus(M0P_UART0, UartRC))         //UART0数据接收
    {
        uartrx_timeout = 5;
        uart_buffer[uart_rxindex++] = Uart_ReceiveData(M0P_UART0);
        if(uart_rxindex == UART_BUFFER_LEN)
            uart_rxindex = 0;
        uart_rxlen++;
        Uart_ClrStatus(M0P_UART0, UartRC);        //清中断状态位
    }
    if(uart_rxlen > 15)
        uart_starttx();
    if(Uart_GetStatus(M0P_UART0, UartTxe))         //UART0数据发送
    {
        if(uart_txlen > 0)
        {
            Uart_SendDataIt(M0P_UART0, uart_buffer[uart_txindex++]);
            if(uart_txindex == UART_BUFFER_LEN)
                uart_txindex = 0;
            uart_txlen--;
        }
        else
        {
            Uart_DisableIrq(M0P_UART0,UartTxEIrq);
        }
        Uart_ClrStatus(M0P_UART0, UartTxe);        //清中断状态位
    }
}
在定时中断中超时计数递减,当减到0后发送剩余的数据
void Tim0_IRQHandler(void)
{
    //Timer0 模式0 溢出中断
    if(TRUE == Bt_GetIntFlag(TIM0, BtUevIrq))
    {
        if(uartrx_timeout > 0)
        {
            uartrx_timeout -= 1;
            if(uartrx_timeout == 0)
                uart_starttx();
        }
        Bt_ClearIntFlag(TIM0,BtUevIrq); //中断标志清零
    }
}
运行效果


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

这里直接沿用,初始化SPI0的IO,这里只初始化SCK MOSI,CS由软件控制输出

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


4.3I2C测试读取DHTC12温湿度
I2C(Inter-Integrated Circuit)是双线双向的同步串行总线,它利用一根时钟线和一根数据线在连接总线的两个器件之间进行信息的传递,为设备之间数据交换提供了一种简单高效的方法
板子定义好了一组I2C的IO

不过这里的PB06参与了别的电路,实测用这组IO也不能完成通讯

换用PB10 PB11

初始化IO


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

初始化DHTC12

void i2c_dev_init()
{
    iic_dev.iictype = YUYY_IIC_TYPE_HARD;
    iic_dev.hiicx = M0P_I2C1;
    dhtc12_dev.iicx = &iic_dev;
    yuyy_dhtc12_init(&dhtc12_dev);
}
main函数中用12864LCD显示温湿度
int32_t main(void)
{
    float HumF,TemF;
    char out[20];
    uint8_t err=0,outlen;
    xth_init();
    //时钟分频设置
    Sysctrl_SetHCLKDiv(SysctrlHclkDiv1);
    Sysctrl_SetPCLKDiv(SysctrlPclkDiv1);
   
    spi_gpio_init();
    spi_init();
    hs12864_init();
   
    i2c_io_init();
    i2c_init();
    i2c_dev_init();
    while(1)
    {
        Spi_SetCS(M0P_SPI0, FALSE);
        err = yuyy_dhtc12_readHT(&dhtc12_dev,&TemF,&HumF);
        outlen = sprintf(out,"%d %.1f%% %.1f",err,HumF,TemF);
        yuyy_hs12864g18b_display_string_8x16(&hs12864_ctr,0,4,0,(uint8_t *)out);
        yuyy_hs12864g18b_display_graphic_16x16(&hs12864_ctr,0,4,8*outlen,(uint8_t *)test16x16temp);
        yuyy_hs12864g18b_display_string_8x16(&hs12864_ctr,0,4,8*(outlen+2),(uint8_t *)"      ");
        Spi_SetCS(M0P_SPI0, TRUE);
        yuyy_delay_ms(1000);
    }
}


运行效果


使用特权

评论回复
沙发
guijial511| | 2023-8-7 11:51 | 只看该作者
12864液晶屏什么时候出SPI接口了

使用特权

评论回复
板凳
yuyy1989|  楼主 | 2023-8-7 15:07 | 只看该作者
guijial511 发表于 2023-8-7 11:51
12864液晶屏什么时候出SPI接口了

应该很早就有了吧,这个屏用的IC文档修改记录最早到07年

使用特权

评论回复
地板
ysf| | 2023-8-7 20:59 | 只看该作者
guijial511 发表于 2023-8-7 11:51
12864液晶屏什么时候出SPI接口了

小彩屏都有很多SPI的了,这种黑白屏更多,前几天淘了几块可以并口、SPI、IIC口的192x96的屏,挺不错,比并口屏接线简单多了

使用特权

评论回复
5
Alohaq| | 2023-10-24 15:44 | 只看该作者
请问有源码分享吗

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

145

主题

695

帖子

6

粉丝