stmf103基于HAL库的串口+DMA接受、发送实现分析

        之前参加比赛使用过USART+DMA的串口通信,最近找工作准备在stm32f103上复习下,结果调了一下午都没成功,最后终于成功通信并通过分析HAL源代码找到问题所在。整个过程主要遇到一下问题:

        1、在串口中断中调用HAL_Delay()函数会卡死

        原因:是因为HAL_Delay()函数是基于sys_clock中断实现,而这个中断优先级不够导致程序在调用出卡死。解决方法可以修改系统时钟的优先级。

        2、在usart1调用DMA发送后必须调用HAL_UART_DMAStop(),不然程序后续无法继续发送

        原因:首先串口配置正常配置,DMA选择普通模式而不是循环模式。此函数源代码如下:

HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)
{
  uint32_t dmarequest = 0x00U;
  /* The Lock is not implemented on this API to allow the user application
     to call the HAL UART API under callbacks HAL_UART_TxCpltCallback() / HAL_UART_RxCpltCallback():
     when calling HAL_DMA_Abort() API the DMA TX/RX Transfer complete interrupt is generated
     and the correspond call back is executed HAL_UART_TxCpltCallback() / HAL_UART_RxCpltCallback()
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是基于 STM32F103 HAL 库的 OLED 显示屏 IIC 驱动代码,供参考: ```c #include "stm32f1xx_hal.h" #include "fonts.h" #define OLED_I2C_ADDR 0x78 // OLED I2C 地址 #define OLED_WIDTH 128 // OLED 宽度 #define OLED_HEIGHT 64 // OLED 高度 I2C_HandleTypeDef hi2c1; void OLED_Init(void) { uint8_t data[] = { 0xAE, // 关闭显示 0xD5, 0x80, // 设置时钟分频因子,震荡频率 0xA8, 0x3F, // 设置驱动路数 0xD3, 0x00, // 设置显示偏移 0x40, // 设置起始行 0x8D, 0x14, // 使能电荷泵 0x20, 0x00, // 水平寻址模式 0xA1, // 设置段重定向 0xC8, // 设置行重定向 0xDA, 0x12, // 设置 COM 硬件引脚配置 0x81, 0xCF, // 设置对比度 0xD9, 0xF1, // 设置预充电周期 0xDB, 0x40, // 设置 VCOMH 电压倍率 0xA4, // 全局显示开启 0xA6, // 设置显示方式,正常显示 0xAF // 开启显示 }; HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x00, 1, data, sizeof(data), 1000); } void OLED_Clear(void) { uint8_t data[OLED_WIDTH * OLED_HEIGHT / 8] = {0}; HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, 1, data, sizeof(data), 1000); } void OLED_SetPos(uint8_t x, uint8_t y) { uint8_t data[] = { 0x00 + x % 16, // 设置列低位地址 0x10 + x / 16, // 设置列高位地址 0xB0 + y, // 设置页地址 }; HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x00, 1, data, sizeof(data), 1000); } void OLED_DrawPixel(uint8_t x, uint8_t y, uint8_t color) { OLED_SetPos(x, y); uint8_t data[] = {color}; HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, 1, data, 1, 1000); } void OLED_DrawChar(uint8_t x, uint8_t y, uint8_t ch, uint8_t size, uint8_t color) { uint8_t i, j; uint8_t data[size * size]; for (i = 0; i < size; i++) { for (j = 0; j < size; j++) { if (size == 12) { data[i * size + j] = pgm_read_byte(&font1206[(ch - 32) * 12 + i]) & (1 << (11 - j)) ? color : 0; } else { data[i * size + j] = pgm_read_byte(&font1608[(ch - 32) * 16 + i]) & (1 << (15 - j)) ? color : 0; } } } OLED_SetPos(x, y); HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, 1, data, size * size, 1000); } void OLED_DrawString(uint8_t x, uint8_t y, char *str, uint8_t size, uint8_t color) { while (*str) { OLED_DrawChar(x, y, *str++, size, color); x += size / 2; } } int main(void) { HAL_Init(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_I2C1_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 400000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; HAL_I2C_Init(&hi2c1); OLED_Init(); OLED_Clear(); OLED_DrawString(0, 0, "Hello World!", 12, 1); while (1) { HAL_Delay(1000); } } ``` 需要注意的是,上述代码中使用了一个 `fonts.h` 文件,它包含了 OLED 显示屏所需的字体数据。可以根据需要更换字体文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值