本帖最后由 yuyy1989 于 2023-8-11 22:39 编辑
#申请原创# @21小跑堂
4.UART通讯+SPI驱动12864LCD+I2C测试
4.1UART通讯测试
UART(Universal Asynchronous Receiver/Transmitter)通用异步收发器,在嵌入式领域应用的非常广泛,选用PB08和PB09作为UART0的TX和RX
初始化IOvoid 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);
}
}
运行效果
|