- 2025-03-25
-
回复了主题帖:
【NUCLEO-U083RE开发板测评】06 STM32U0驱动液晶屏
Jacktang 发表于 2025-3-23 08:53
可以, 还介绍了 液晶屏显示的来龙去脉
多谢大佬鼓励
- 2025-03-21
-
发表了主题帖:
【NUCLEO-U083RE开发板测评】06 STM32U0驱动液晶屏
本帖最后由 怀揣少年梦 于 2025-3-21 16:41 编辑
LCD液晶屏是众多嵌入式开发工程师常用屏幕之一,这次测评也使用LCD液晶进行测试
一、LCD简介
LCD(Liquid Crystal Display,液晶显示器)是一种利用液晶材料的光电特性实现图像显示的平板显示技术。其核心原理是通过电场控制液晶分子的排列,从而改变光的偏振状态,配合背光源和滤光片生成图像
1、技术特性:
结构组成:主要包括液晶层、偏振膜、玻璃基板、驱动电路及背光模组。偏振膜垂直排列,通过电场控制液晶分子扭转角度,调节透光量
核心优势:低功耗(1-10微瓦/平方厘米)、轻薄化(厚度可低至5厘米)、无辐射、高分辨率
技术分类:TN(扭曲向列型)、STN(超扭曲向列型)、TFT(薄膜晶体管型)等,其中TFT-LCD因响应速度和色彩表现优异,成为主流。
2、发展历程:
19世纪末奥地利学者发现液晶特性,20世纪70年代首款TN-LCD应用于电子表、计算器;
1998年后逐步取代CRT显示器,成为计算机、电视等领域的标准配置。
二、LCD液晶屏的主要用途
LCD凭借其技术优势,覆盖从消费电子到工业设备的广泛领域:
1、图像显示
基础功能:通过像素阵列呈现静态或动态画面,支持彩色滤光片实现全彩显示
信号兼容性:支持模拟与数字信号输入,需通过APC模块转换信号类型
2、信息交互:
作为人机界面(HMI),用于触控操作、参数设置及数据反馈
三、实际应用
本次使用U083开发板驱动2.5寸 TFT-LCD 显示屏(ILI9341驱动芯片);
1、工程配置
1)配置时钟
2)配置相关SPI接口
2、移植相关代码
相关LCD代码
/* [url=home.php?mod=space&uid=1020061]@attention[/url] *
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, QD electronic SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
**************************************************************************************************/
#include "lcd.h"
#include "spi.h"
#include <stdio.h>
//管理LCD重要参数
//默认为竖屏
_lcd_dev lcddev;
//画笔颜色,背景颜色
uint16_t POINT_COLOR = 0x0000,BACK_COLOR = 0xFFFF;
uint16_t DeviceCode;
uint8_t LCD_SPI_WriteByte(uint8_t TxData) {
uint8_t RxData = 0X00;
if(HAL_SPI_TransmitReceive(LCD_SPI_Handle, &TxData, &RxData, 1, 100) != HAL_OK)
{
RxData = 0XFF;
}
return RxData;
}
/*****************************************************************************
* [url=home.php?mod=space&uid=32621]@name[/url] :void LCD_WR_REG(uint8_t data)
* [url=home.php?mod=space&uid=311857]@date[/url] :2018-08-09
* [url=home.php?mod=space&uid=665173]@FUNCTION[/url] :Write an 8-bit command to the LCD screen
* @parameters :data:Command value to be written
* @retvalue :None
******************************************************************************/
void LCD_WR_REG(uint8_t data)
{
LCD_CS_CLR;
LCD_RS_CLR;
LCD_SPI_WriteByte(data);
LCD_CS_SET;
}
/*****************************************************************************
* @name :void LCD_WR_DATA(uint8_t data)
* @date :2018-08-09
* @function :Write an 8-bit data to the LCD screen
* @parameters :data:data value to be written
* @retvalue :None
******************************************************************************/
void LCD_WR_DATA(uint8_t data)
{
LCD_CS_CLR;
LCD_RS_SET;
LCD_SPI_WriteByte(data);
LCD_CS_SET;
}
/*****************************************************************************
* @name :void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
* @date :2018-08-09
* @function :Write data into registers
* @parameters :LCD_Reg:Register address
LCD_RegValue:Data to be written
* @retvalue :None
******************************************************************************/
void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
{
LCD_WR_REG(LCD_Reg);
LCD_WR_DATA(LCD_RegValue);
}
/*****************************************************************************
* @name :void LCD_WriteRAM_Prepare(void)
* @date :2018-08-09
* @function :Write GRAM
* @parameters :None
* @retvalue :None
******************************************************************************/
void LCD_WriteRAM_Prepare(void)
{
LCD_WR_REG(lcddev.wramcmd);
}
/*****************************************************************************
* @name :void Lcd_WriteData_16Bit(uint16_t Data)
* @date :2018-08-09
* @function :Write an 16-bit command to the LCD screen
* @parameters :Data:Data to be written
* @retvalue :None
******************************************************************************/
void Lcd_WriteData_16Bit(uint16_t Data)
{
LCD_CS_CLR;
LCD_RS_SET;
LCD_SPI_WriteByte(Data>>8);
LCD_SPI_WriteByte(Data);
LCD_CS_SET;
}
/*****************************************************************************
* @name :void LCD_DrawPoint(uint16_t x,uint16_t y)
* @date :2018-08-09
* @function :Write a pixel data at a specified location
* @parameters :x:the x coordinate of the pixel
y:the y coordinate of the pixel
* @retvalue :None
******************************************************************************/
void LCD_DrawPoint(uint16_t x,uint16_t y)
{
LCD_SetCursor(x,y);//设置光标位置
Lcd_WriteData_16Bit(POINT_COLOR);
}
/*****************************************************************************
* @name :void LCD_Clear(uint16_t Color)
* @date :2018-08-09
* @function :Full screen filled LCD screen
* @parameters :color:Filled color
* @retvalue :None
******************************************************************************/
void LCD_Clear(uint16_t Color)
{
unsigned int i,m;
LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);
LCD_CS_CLR;
LCD_RS_SET;
for(i=0;i<lcddev.height;i++)
{
for(m=0;m<lcddev.width;m++)
{
Lcd_WriteData_16Bit(Color);
}
}
LCD_CS_SET;
}
/*****************************************************************************
* @name :void LCD_RESET(void)
* @date :2018-08-09
* @function :Reset LCD screen
* @parameters :None
* @retvalue :None
******************************************************************************/
void LCD_RESET(void)
{
LCD_RST_CLR;
HAL_Delay(100);
LCD_RST_SET;
HAL_Delay(50);
}
/*****************************************************************************
* @name :void LCD_RESET(void)
* @date :2018-08-09
* @function :Initialization LCD screen
* @parameters :None
* @retvalue :None
******************************************************************************/
void LCD_Init(void)
{
LCD_RESET(); //LCD 复位
//*************3.2inch ILI9341初始化**********//
LCD_WR_REG(0xCF);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0xD9); //C1
LCD_WR_DATA(0X30);
LCD_WR_REG(0xED);
LCD_WR_DATA(0x64);
LCD_WR_DATA(0x03);
LCD_WR_DATA(0X12);
LCD_WR_DATA(0X81);
LCD_WR_REG(0xE8);
LCD_WR_DATA(0x85);
LCD_WR_DATA(0x10);
LCD_WR_DATA(0x7A);
LCD_WR_REG(0xCB);
LCD_WR_DATA(0x39);
LCD_WR_DATA(0x2C);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x34);
LCD_WR_DATA(0x02);
LCD_WR_REG(0xF7);
LCD_WR_DATA(0x20);
LCD_WR_REG(0xEA);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x00);
LCD_WR_REG(0xC0); //Power control
LCD_WR_DATA(0x1B); //VRH[5:0]
LCD_WR_REG(0xC1); //P0
LCD_WR_DATA(0x12); //SAP[2:0];BT[3:0] //0x01
LCD_WR_REG(0xC5); //VCM control
LCD_WR_DATA(0x26); //3F
LCD_WR_DATA(0x26); //3C
LCD_WR_REG(0xC7); //VCM control2
LCD_WR_DATA(0XB0);
LCD_WR_REG(0x36); // Memory Access Control
LCD_WR_DATA(0x08);
LCD_WR_REG(0x3A);
LCD_WR_DATA(0x55);
LCD_WR_REG(0xB1);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x1A);
LCD_WR_REG(0xB6); // Display Function Control
LCD_WR_DATA(0x0A);
LCD_WR_DATA(0xA2);
LCD_WR_REG(0xF2); // 3Gamma Function Disable
LCD_WR_DATA(0x00);
LCD_WR_REG(0x26); //Gamma curve selected
LCD_WR_DATA(0x01);
LCD_WR_REG(0xE0); //Set Gamma
LCD_WR_DATA(0x1F);
LCD_WR_DATA(0x24);
LCD_WR_DATA(0x24);
LCD_WR_DATA(0x0D);
LCD_WR_DATA(0x12);
LCD_WR_DATA(0x09);
LCD_WR_DATA(0x52);
LCD_WR_DATA(0xB7);
LCD_WR_DATA(0x3F);
LCD_WR_DATA(0x0C);
LCD_WR_DATA(0x15);
LCD_WR_DATA(0x06);
LCD_WR_DATA(0x0E);
LCD_WR_DATA(0x08);
LCD_WR_DATA(0x00);
LCD_WR_REG(0XE1); //Set Gamma
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x1B);
LCD_WR_DATA(0x1B);
LCD_WR_DATA(0x02);
LCD_WR_DATA(0x0E);
LCD_WR_DATA(0x06);
LCD_WR_DATA(0x2E);
LCD_WR_DATA(0x48);
LCD_WR_DATA(0x3F);
LCD_WR_DATA(0x03);
LCD_WR_DATA(0x0A);
LCD_WR_DATA(0x09);
LCD_WR_DATA(0x31);
LCD_WR_DATA(0x37);
LCD_WR_DATA(0x1F);
LCD_WR_REG(0x2B);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x01);
LCD_WR_DATA(0x3f);
LCD_WR_REG(0x2A);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0xef);
LCD_WR_REG(0x11); //Exit Sleep
HAL_Delay(120);
LCD_WR_REG(0x29); //display on
LCD_direction(USE_HORIZONTAL);//设置LCD显示方向
LCD_BL_ON;//点亮背光
LCD_Clear(GREEN);//清全屏白色
}
/*****************************************************************************
* @name :void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd)
* @date :2018-08-09
* @function :Setting LCD display window
* @parameters :xStar:the bebinning x coordinate of the LCD display window
yStar:the bebinning y coordinate of the LCD display window
xEnd:the endning x coordinate of the LCD display window
yEnd:the endning y coordinate of the LCD display window
* @retvalue :None
******************************************************************************/
void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd)
{
LCD_WR_REG(lcddev.setxcmd);
LCD_WR_DATA(xStar>>8);
LCD_WR_DATA(0x00FF&xStar);
LCD_WR_DATA(xEnd>>8);
LCD_WR_DATA(0x00FF&xEnd);
LCD_WR_REG(lcddev.setycmd);
LCD_WR_DATA(yStar>>8);
LCD_WR_DATA(0x00FF&yStar);
LCD_WR_DATA(yEnd>>8);
LCD_WR_DATA(0x00FF&yEnd);
LCD_WriteRAM_Prepare(); //开始写入GRAM
}
/*****************************************************************************
* @name :void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
* @date :2018-08-09
* @function :Set coordinate value
* @parameters :Xpos:the x coordinate of the pixel
Ypos:the y coordinate of the pixel
* @retvalue :None
******************************************************************************/
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
{
LCD_SetWindows(Xpos,Ypos,Xpos,Ypos);
}
/*****************************************************************************
* @name :void LCD_direction(uint8_t direction)
* @date :2018-08-09
* @function :Setting the display direction of LCD screen
* @parameters :direction:0-0 degree
1-90 degree
2-180 degree
3-270 degree
* @retvalue :None
******************************************************************************/
void LCD_direction(uint8_t direction)
{
lcddev.setxcmd=0x2A;
lcddev.setycmd=0x2B;
lcddev.wramcmd=0x2C;
switch(direction){
case 0:
lcddev.width=LCD_W;
lcddev.height=LCD_H;
LCD_WriteReg(0x36,(1<<3)|(0<<6)|(0<<7));//BGR==1,MY==0,MX==0,MV==0
break;
case 1:
lcddev.width=LCD_H;
lcddev.height=LCD_W;
LCD_WriteReg(0x36,(1<<3)|(0<<7)|(1<<6)|(1<<5));//BGR==1,MY==1,MX==0,MV==1
break;
case 2:
lcddev.width=LCD_W;
lcddev.height=LCD_H;
LCD_WriteReg(0x36,(1<<3)|(1<<6)|(1<<7));//BGR==1,MY==0,MX==0,MV==0
break;
case 3:
lcddev.width=LCD_H;
lcddev.height=LCD_W;
LCD_WriteReg(0x36,(1<<3)|(1<<7)|(1<<5));//BGR==1,MY==1,MX==0,MV==1
break;
default:break;
}
}
相关GUI代码
/* @attention
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, QD electronic SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
**************************************************************************************************/
#include "lcd.h"
#include "string.h"
#include "gui.h"
#include "font.h"
/*******************************************************************
* @name :void GUI_DrawPoint(uint16_t x,uint16_t y,uint16_t color)
* @date :2018-08-09
* @function :draw a point in LCD screen
* @parameters :x:the x coordinate of the point
y:the y coordinate of the point
color:the color value of the point
* @retvalue :None
********************************************************************/
void GUI_DrawPoint(uint16_t x,uint16_t y,uint16_t color)
{
LCD_SetCursor(x,y);//设置光标位置
Lcd_WriteData_16Bit(color);
}
/*******************************************************************
* @name :void LCD_Fill(uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey,uint16_t color)
* @date :2018-08-09
* @function :fill the specified area
* @parameters :sx:the bebinning x coordinate of the specified area
sy:the bebinning y coordinate of the specified area
ex:the ending x coordinate of the specified area
ey:the ending y coordinate of the specified area
color:the filled color value
* @retvalue :None
********************************************************************/
void LCD_Fill(uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey,uint16_t color)
{
uint16_t i,j;
uint16_t width=ex-sx+1; //得到填充的宽度
uint16_t height=ey-sy+1; //高度
LCD_SetWindows(sx,sy,ex,ey);//设置显示窗口
for(i=0;i<height;i++)
{
for(j=0;j<width;j++)
Lcd_WriteData_16Bit(color); //写入数据
}
LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复窗口设置为全屏
}
/*******************************************************************
* @name :void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
* @date :2018-08-09
* @function :Draw a line between two points
* @parameters :x1:the bebinning x coordinate of the line
y1:the bebinning y coordinate of the line
x2:the ending x coordinate of the line
y2:the ending y coordinate of the line
* @retvalue :None
********************************************************************/
void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
uint16_t t;
int xerr=0,yerr=0,delta_x,delta_y,distance;
int incx,incy,uRow,uCol;
delta_x=x2-x1; //计算坐标增量
delta_y=y2-y1;
uRow=x1;
uCol=y1;
if(delta_x>0)incx=1; //设置单步方向
else if(delta_x==0)incx=0;//垂直线
else {incx=-1;delta_x=-delta_x;}
if(delta_y>0)incy=1;
else if(delta_y==0)incy=0;//水平线
else{incy=-1;delta_y=-delta_y;}
if( delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴
else distance=delta_y;
for(t=0;t<=distance+1;t++ )//画线输出
{
LCD_DrawPoint(uRow,uCol);//画点
xerr+=delta_x ;
yerr+=delta_y ;
if(xerr>distance)
{
xerr-=distance;
uRow+=incx;
}
if(yerr>distance)
{
yerr-=distance;
uCol+=incy;
}
}
}
/*****************************************************************************
* @name :void LCD_DrawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
* @date :2018-08-09
* @function :Draw a rectangle
* @parameters :x1:the bebinning x coordinate of the rectangle
y1:the bebinning y coordinate of the rectangle
x2:the ending x coordinate of the rectangle
y2:the ending y coordinate of the rectangle
* @retvalue :None
******************************************************************************/
void LCD_DrawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
LCD_DrawLine(x1,y1,x2,y1);
LCD_DrawLine(x1,y1,x1,y2);
LCD_DrawLine(x1,y2,x2,y2);
LCD_DrawLine(x2,y1,x2,y2);
}
/*****************************************************************************
* @name :void LCD_DrawFillRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
* @date :2018-08-09
* @function :Filled a rectangle
* @parameters :x1:the bebinning x coordinate of the filled rectangle
y1:the bebinning y coordinate of the filled rectangle
x2:the ending x coordinate of the filled rectangle
y2:the ending y coordinate of the filled rectangle
* @retvalue :None
******************************************************************************/
void LCD_DrawFillRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
LCD_Fill(x1,y1,x2,y2,POINT_COLOR);
}
/*****************************************************************************
* @name :void _draw_circle_8(int xc, int yc, int x, int y, uint16_t c)
* @date :2018-08-09
* @function :8 symmetry circle drawing algorithm (internal call)
* @parameters :xc:the x coordinate of the Circular center
yc:the y coordinate of the Circular center
x:the x coordinate relative to the Circular center
y:the y coordinate relative to the Circular center
c:the color value of the circle
* @retvalue :None
******************************************************************************/
void _draw_circle_8(int xc, int yc, int x, int y, uint16_t c)
{
GUI_DrawPoint(xc + x, yc + y, c);
GUI_DrawPoint(xc - x, yc + y, c);
GUI_DrawPoint(xc + x, yc - y, c);
GUI_DrawPoint(xc - x, yc - y, c);
GUI_DrawPoint(xc + y, yc + x, c);
GUI_DrawPoint(xc - y, yc + x, c);
GUI_DrawPoint(xc + y, yc - x, c);
GUI_DrawPoint(xc - y, yc - x, c);
}
/*****************************************************************************
* @name :void gui_circle(int xc, int yc,uint16_t c,int r, int fill)
* @date :2018-08-09
* @function :Draw a circle of specified size at a specified location
* @parameters :xc:the x coordinate of the Circular center
yc:the y coordinate of the Circular center
r:Circular radius
fill:1-filling,0-no filling
* @retvalue :None
******************************************************************************/
void gui_circle(int xc, int yc,uint16_t c,int r, int fill)
{
int x = 0, y = r, yi, d;
d = 3 - 2 * r;
if (fill)
{
// 如果填充(画实心圆)
while (x <= y) {
for (yi = x; yi <= y; yi++)
_draw_circle_8(xc, yc, x, yi, c);
if (d < 0) {
d = d + 4 * x + 6;
} else {
d = d + 4 * (x - y) + 10;
y--;
}
x++;
}
} else
{
// 如果不填充(画空心圆)
while (x <= y) {
_draw_circle_8(xc, yc, x, y, c);
if (d < 0) {
d = d + 4 * x + 6;
} else {
d = d + 4 * (x - y) + 10;
y--;
}
x++;
}
}
}
/*****************************************************************************
* @name :void Draw_Triangel(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
* @date :2018-08-09
* @function :Draw a triangle at a specified position
* @parameters :x0:the bebinning x coordinate of the triangular edge
y0:the bebinning y coordinate of the triangular edge
x1:the vertex x coordinate of the triangular
y1:the vertex y coordinate of the triangular
x2:the ending x coordinate of the triangular edge
y2:the ending y coordinate of the triangular edge
* @retvalue :None
******************************************************************************/
void Draw_Triangel(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
{
LCD_DrawLine(x0,y0,x1,y1);
LCD_DrawLine(x1,y1,x2,y2);
LCD_DrawLine(x2,y2,x0,y0);
}
static void _swap(uint16_t *a, uint16_t *b)
{
uint16_t tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
/*****************************************************************************
* @name :void Fill_Triangel(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
* @date :2018-08-09
* @function :filling a triangle at a specified position
* @parameters :x0:the bebinning x coordinate of the triangular edge
y0:the bebinning y coordinate of the triangular edge
x1:the vertex x coordinate of the triangular
y1:the vertex y coordinate of the triangular
x2:the ending x coordinate of the triangular edge
y2:the ending y coordinate of the triangular edge
* @retvalue :None
******************************************************************************/
void Fill_Triangel(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
{
uint16_t a, b, y, last;
int dx01, dy01, dx02, dy02, dx12, dy12;
long sa = 0;
long sb = 0;
if (y0 > y1)
{
_swap(&y0,&y1);
_swap(&x0,&x1);
}
if (y1 > y2)
{
_swap(&y2,&y1);
_swap(&x2,&x1);
}
if (y0 > y1)
{
_swap(&y0,&y1);
_swap(&x0,&x1);
}
if(y0 == y2)
{
a = b = x0;
if(x1 < a)
{
a = x1;
}
else if(x1 > b)
{
b = x1;
}
if(x2 < a)
{
a = x2;
}
else if(x2 > b)
{
b = x2;
}
LCD_Fill(a,y0,b,y0,POINT_COLOR);
return;
}
dx01 = x1 - x0;
dy01 = y1 - y0;
dx02 = x2 - x0;
dy02 = y2 - y0;
dx12 = x2 - x1;
dy12 = y2 - y1;
if(y1 == y2)
{
last = y1;
}
else
{
last = y1-1;
}
for(y=y0; y<=last; y++)
{
a = x0 + sa / dy01;
b = x0 + sb / dy02;
sa += dx01;
sb += dx02;
if(a > b)
{
_swap(&a,&b);
}
LCD_Fill(a,y,b,y,POINT_COLOR);
}
sa = dx12 * (y - y1);
sb = dx02 * (y - y0);
for(; y<=y2; y++)
{
a = x1 + sa / dy12;
b = x0 + sb / dy02;
sa += dx12;
sb += dx02;
if(a > b)
{
_swap(&a,&b);
}
LCD_Fill(a,y,b,y,POINT_COLOR);
}
}
/*****************************************************************************
* @name :void LCD_ShowChar(uint16_t x,uint16_t y,uint16_t fc, uint16_t bc, uint8_t num,uint8_t size,uint8_t mode)
* @date :2018-08-09
* @function :Display a single English character
* @parameters :x:the bebinning x coordinate of the Character display position
y:the bebinning y coordinate of the Character display position
fc:the color value of display character
bc:the background color of display character
num:the ascii code of display character(0~94)
size:the size of display character
mode:0-no overlying,1-overlying
* @retvalue :None
******************************************************************************/
void LCD_ShowChar(uint16_t x,uint16_t y,uint16_t fc, uint16_t bc, uint8_t num,uint8_t size,uint8_t mode)
{
uint8_t temp;
uint8_t pos,t;
uint16_t colortemp=POINT_COLOR;
num=num-' ';//得到偏移后的值
LCD_SetWindows(x,y,x+size/2-1,y+size-1);//设置单个文字显示窗口
if(!mode) //非叠加方式
{
for(pos=0;pos<size;pos++)
{
if(size==12)temp=asc2_1206[num][pos];//调用1206字体
else temp=asc2_1608[num][pos]; //调用1608字体
for(t=0;t<size/2;t++)
{
if(temp&0x01)Lcd_WriteData_16Bit(fc);
else Lcd_WriteData_16Bit(bc);
temp>>=1;
}
}
}else//叠加方式
{
for(pos=0;pos<size;pos++)
{
if(size==12)temp=asc2_1206[num][pos];//调用1206字体
else temp=asc2_1608[num][pos]; //调用1608字体
for(t=0;t<size/2;t++)
{
POINT_COLOR=fc;
if(temp&0x01)LCD_DrawPoint(x+t,y+pos);//画一个点
temp>>=1;
}
}
}
POINT_COLOR=colortemp;
LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复窗口为全屏
}
/*****************************************************************************
* @name :void LCD_ShowString(uint16_t x,uint16_t y,uint8_t size,uint8_t *p,uint8_t mode)
* @date :2018-08-09
* @function :Display English string
* @parameters :x:the bebinning x coordinate of the English string
y:the bebinning y coordinate of the English string
p:the start address of the English string
size:the size of display character
mode:0-no overlying,1-overlying
* @retvalue :None
******************************************************************************/
void LCD_ShowString(uint16_t x,uint16_t y,uint8_t size,uint8_t *p,uint8_t mode)
{
while((*p<='~')&&(*p>=' '))//判断是不是非法字符!
{
if(x>(lcddev.width-1)||y>(lcddev.height-1))
return;
LCD_ShowChar(x,y,POINT_COLOR,BACK_COLOR,*p,size,mode);
x+=size/2;
p++;
}
}
/*****************************************************************************
* @name :uint32_t mypow(uint8_t m,uint8_t n)
* @date :2018-08-09
* @function :get the nth power of m (internal call)
* @parameters :m:the multiplier
n:the power
* @retvalue :the nth power of m
******************************************************************************/
uint32_t mypow(uint8_t m,uint8_t n)
{
uint32_t result=1;
while(n--)result*=m;
return result;
}
/*****************************************************************************
* @name :void LCD_ShowNum(uint16_t x,uint16_t y,uint32_t num,uint8_t len,uint8_t size)
* @date :2018-08-09
* @function :Display number
* @parameters :x:the bebinning x coordinate of the number
y:the bebinning y coordinate of the number
num:the number(0~4294967295)
len:the length of the display number
size:the size of display number
* @retvalue :None
******************************************************************************/
void LCD_ShowNum(uint16_t x,uint16_t y,uint32_t num,uint8_t len,uint8_t size)
{
uint8_t t,temp;
uint8_t enshow=0;
for(t=0;t<len;t++)
{
temp=(num/mypow(10,len-t-1))%10;
if(enshow==0&&t<(len-1))
{
if(temp==0)
{
LCD_ShowChar(x+(size/2)*t,y,POINT_COLOR,BACK_COLOR,' ',size,0);
continue;
}else enshow=1;
}
LCD_ShowChar(x+(size/2)*t,y,POINT_COLOR,BACK_COLOR,temp+'0',size,0);
}
}
/*****************************************************************************
* @name :void GUI_DrawFont16(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s,uint8_t mode)
* @date :2018-08-09
* @function :Display a single 16x16 Chinese character
* @parameters :x:the bebinning x coordinate of the Chinese character
y:the bebinning y coordinate of the Chinese character
fc:the color value of Chinese character
bc:the background color of Chinese character
s:the start address of the Chinese character
mode:0-no overlying,1-overlying
* @retvalue :None
******************************************************************************/
void GUI_DrawFont16(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s,uint8_t mode)
{
uint8_t i,j;
uint16_t k;
uint16_t HZnum;
uint16_t x0=x;
HZnum=sizeof(tfont16)/sizeof(typFNT_GB16); //自动统计汉字数目
for (k=0;k<HZnum;k++)
{
if ((tfont16[k].Index[0]==*(s))&&(tfont16[k].Index[1]==*(s+1)))
{ LCD_SetWindows(x,y,x+16-1,y+16-1);
for(i=0;i<16*2;i++)
{
for(j=0;j<8;j++)
{
if(!mode) //非叠加方式
{
if(tfont16[k].Msk[i]&(0x80>>j)) Lcd_WriteData_16Bit(fc);
else Lcd_WriteData_16Bit(bc);
}
else
{
POINT_COLOR=fc;
if(tfont16[k].Msk[i]&(0x80>>j)) LCD_DrawPoint(x,y);//画一个点
x++;
if((x-x0)==16)
{
x=x0;
y++;
break;
}
}
}
}
}
continue; //查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
}
LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复窗口为全屏
}
/*****************************************************************************
* @name :void GUI_DrawFont24(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s,uint8_t mode)
* @date :2018-08-09
* @function :Display a single 24x24 Chinese character
* @parameters :x:the bebinning x coordinate of the Chinese character
y:the bebinning y coordinate of the Chinese character
fc:the color value of Chinese character
bc:the background color of Chinese character
s:the start address of the Chinese character
mode:0-no overlying,1-overlying
* @retvalue :None
******************************************************************************/
void GUI_DrawFont24(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s,uint8_t mode)
{
uint8_t i,j;
uint16_t k;
uint16_t HZnum;
uint16_t x0=x;
HZnum=sizeof(tfont24)/sizeof(typFNT_GB24); //自动统计汉字数目
for (k=0;k<HZnum;k++)
{
if ((tfont24[k].Index[0]==*(s))&&(tfont24[k].Index[1]==*(s+1)))
{ LCD_SetWindows(x,y,x+24-1,y+24-1);
for(i=0;i<24*3;i++)
{
for(j=0;j<8;j++)
{
if(!mode) //非叠加方式
{
if(tfont24[k].Msk[i]&(0x80>>j)) Lcd_WriteData_16Bit(fc);
else Lcd_WriteData_16Bit(bc);
}
else
{
POINT_COLOR=fc;
if(tfont24[k].Msk[i]&(0x80>>j)) LCD_DrawPoint(x,y);//画一个点
x++;
if((x-x0)==24)
{
x=x0;
y++;
break;
}
}
}
}
}
continue; //查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
}
LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复窗口为全屏
}
/*****************************************************************************
* @name :void GUI_DrawFont32(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s,uint8_t mode)
* @date :2018-08-09
* @function :Display a single 32x32 Chinese character
* @parameters :x:the bebinning x coordinate of the Chinese character
y:the bebinning y coordinate of the Chinese character
fc:the color value of Chinese character
bc:the background color of Chinese character
s:the start address of the Chinese character
mode:0-no overlying,1-overlying
* @retvalue :None
******************************************************************************/
void GUI_DrawFont32(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *s,uint8_t mode)
{
uint8_t i,j;
uint16_t k;
uint16_t HZnum;
uint16_t x0=x;
HZnum=sizeof(tfont32)/sizeof(typFNT_GB32); //自动统计汉字数目
for (k=0;k<HZnum;k++)
{
if ((tfont32[k].Index[0]==*(s))&&(tfont32[k].Index[1]==*(s+1)))
{ LCD_SetWindows(x,y,x+32-1,y+32-1);
for(i=0;i<32*4;i++)
{
for(j=0;j<8;j++)
{
if(!mode) //非叠加方式
{
if(tfont32[k].Msk[i]&(0x80>>j)) Lcd_WriteData_16Bit(fc);
else Lcd_WriteData_16Bit(bc);
}
else
{
POINT_COLOR=fc;
if(tfont32[k].Msk[i]&(0x80>>j)) LCD_DrawPoint(x,y);//画一个点
x++;
if((x-x0)==32)
{
x=x0;
y++;
break;
}
}
}
}
}
continue; //查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
}
LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复窗口为全屏
}
/*****************************************************************************
* @name :void Show_Str(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *str,uint8_t size,uint8_t mode)
* @date :2018-08-09
* @function :Display Chinese and English strings
* @parameters :x:the bebinning x coordinate of the Chinese and English strings
y:the bebinning y coordinate of the Chinese and English strings
fc:the color value of Chinese and English strings
bc:the background color of Chinese and English strings
str:the start address of the Chinese and English strings
size:the size of Chinese and English strings
mode:0-no overlying,1-overlying
* @retvalue :None
******************************************************************************/
void Show_Str(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *str,uint8_t size,uint8_t mode)
{
uint16_t x0=x;
uint8_t bHz=0; //字符或者中文
while(*str!=0)//数据未结束
{
if(!bHz)
{
if(x>(lcddev.width-size/2)||y>(lcddev.height-size))
return;
if(*str>0x80)bHz=1;//中文
else //字符
{
if(*str==0x0D)//换行符号
{
y+=size;
x=x0;
str++;
}
else
{
if(size>16)//字库中没有集成12X24 16X32的英文字体,用8X16代替
{
LCD_ShowChar(x,y,fc,bc,*str,16,mode);
x+=8; //字符,为全字的一半
}
else
{
LCD_ShowChar(x,y,fc,bc,*str,size,mode);
x+=size/2; //字符,为全字的一半
}
}
str++;
}
}else//中文
{
if(x>(lcddev.width-size)||y>(lcddev.height-size))
return;
bHz=0;//有汉字库
if(size==32)
GUI_DrawFont32(x,y,fc,bc,str,mode);
else if(size==24)
GUI_DrawFont24(x,y,fc,bc,str,mode);
else
GUI_DrawFont16(x,y,fc,bc,str,mode);
str+=2;
x+=size;//下一个汉字偏移
}
}
}
/*****************************************************************************
* @name :void Gui_StrCenter(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *str,uint8_t size,uint8_t mode)
* @date :2018-08-09
* @function :Centered display of English and Chinese strings
* @parameters :x:the bebinning x coordinate of the Chinese and English strings
y:the bebinning y coordinate of the Chinese and English strings
fc:the color value of Chinese and English strings
bc:the background color of Chinese and English strings
str:the start address of the Chinese and English strings
size:the size of Chinese and English strings
mode:0-no overlying,1-overlying
* @retvalue :None
******************************************************************************/
void Gui_StrCenter(uint16_t x, uint16_t y, uint16_t fc, uint16_t bc, uint8_t *str,uint8_t size,uint8_t mode)
{
uint16_t len=strlen((const char *)str);
uint16_t x1=(lcddev.width-len*8)/2;
Show_Str(x1,y,fc,bc,str,size,mode);
}
/*****************************************************************************
* @name :void Gui_Drawbmp16(uint16_t x,uint16_t y,const unsigned char *p)
* @date :2018-08-09
* @function :Display a 16-bit BMP image
* @parameters :x:the bebinning x coordinate of the BMP image
y:the bebinning y coordinate of the BMP image
p:the start address of image array
* @retvalue :None
******************************************************************************/
void Gui_Drawbmp16(uint16_t x,uint16_t y,const unsigned char *p) //显示40*40 QQ图片
{
int i;
unsigned char picH,picL;
LCD_SetWindows(x,y,x+240-1,y+240-1);//窗口设置
for(i=0;i<240*240;i++)
{
picL=*(p+i*2); //数据低位在前
picH=*(p+i*2+1);
Lcd_WriteData_16Bit(picH<<8|picL);
}
LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复显示窗口为全屏
}
3、编译下载验证
实际现象,如下视频
源码:
总结:实际测试刷新率不是特别高,受限于时钟频率
- 2025-02-28
-
回复了主题帖:
【NUCLEO-U083RE开发板测评】05 STM32U0低功耗定时器
wangerxian 发表于 2025-2-28 09:16
那确实还是低功耗好。低功耗定时器有什么其他不足吗?
不足之处及有以下几点:
1、通道没有高级定时器多;
2、不能进行互补输出功能;
- 2025-02-27
-
回复了主题帖:
【NUCLEO-U083RE开发板测评】05 STM32U0低功耗定时器
wangerxian 发表于 2025-2-26 21:22
其实想知道低功耗定时器和普通定时器功耗能差多少0.0
低功耗定时器在PWM模式时,只有2.4uA,在运行模式普通定时器,至少有400uA,差距还是挺大的
- 2025-02-26
-
发表了主题帖:
【NUCLEO-U083RE开发板测评】05 STM32U0低功耗定时器
本帖最后由 怀揣少年梦 于 2025-2-26 15:04 编辑
本次测评来测试一下低功耗定时器。
一、低功耗定时器简介
1、是什么
LPTIM称为低功耗定时器,LPTIM 是一个 16 位定时器,其时钟源具有多样性,因此LPTIM 能够在 STM32U0 微控制器的大多数低功耗模式下保持运行状态。
具备以下特点:
1)具备多选独立时钟源方案
内部时钟源:LSE/LSI/HSI16/APB时钟;
外部时钟源:可以使用LPTIM_IN1的时钟作为时钟源
2)高达8个外部触发器
具有可配置的有效边沿:上升沿、下降沿和双边沿
带有数字毛刺滤波器以避免虚假触发
3)2 种操作模式
连续模式和单次模式
4)多通道
最多4个通道
5)能够在STOP模式下唤醒
功能框图如图
2、适用什么环境
主要应用于低功耗场景,如低功耗周期性任务、PWM控制、传感器数据采集等。
二、应用
低功耗定时器产生PWM
1)配置时钟,选择HSI为LPTIM时钟源
2)打开和配置定时器
3)编写代码
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_LPTIM1_Init();
/* USER CODE BEGIN 2 */
HAL_Delay(5000);
if (HAL_LPTIM_PWM_Start(&hlptim1, LPTIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
/* Suspend Tick */
HAL_SuspendTick();
/* Enter in Stop mode */
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
HAL_ResumeTick();
HAL_LPTIM_PWM_Stop(&hlptim1, LPTIM_CHANNEL_3);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
4)测试验证
波形图:
功耗
源码
- 2025-02-12
-
发表了主题帖:
【NUCLEO-U083RE开发板测评】04 STM32U0功耗有几何
本次测评主要测量一下STM32U083进入低功耗模式的功耗情况.
一、U083有哪几种功耗模式
根据U083的参考手册,总共有7中低功耗模式:
睡眠模式、低功耗运行模式、低功耗休眠模式、停止模式0、停止模式1、停止模式2、待机模式、关断模式;
简单介绍一下U083几种功耗模式。
1、睡眠模式
CPU CLK 被关断,所有外设正常运行
2、低功耗运行模式
前提条件:时钟低于2Mhz;主要将内部电源进入低功耗模式实现
3、低功耗睡眠模式
从低功耗运行模式下进入该模式
4、停止模式
停止模式主要是关闭所有时钟(MSI\PLL\HSI16\HSE),RTC可以保持运行,一些其他外设可以有条件的运行,低功耗调整器还在运行
5、待机模式
低功耗调整器可以选择不运行,所有时钟被关闭。
6、关断模式
低功耗调整器关闭,所有时钟被关闭。
各个模式之间相互切换的关系图
各模式对比如下
二、功耗测试
1、编写测试代码
/* USER CODE BEGIN Header */
/**
******************************************************************************
* [url=home.php?mod=space&uid=1307177]@File[/url] : main.c
* [url=home.php?mod=space&uid=159083]@brief[/url] : Main program body
******************************************************************************
* [url=home.php?mod=space&uid=1020061]@attention[/url] *
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define RUN_MODE 0
#define SLEEP_MODE 0
#define LP_RUN_MODE 1
#define LP_SLEEP_MODE 0
#define STOP_MODE_0 0
#define STOP_MODE_1 0
#define STOP_MODE_2 0
#define STANDY_MODE 0
#define SHUTDOWN_MODE 0
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
static void SYSCLKConfig_WAKEUP(void);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
GPIO_InitTypeDef GPIO_InitStruct;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* Enable Power Clock */
__HAL_RCC_PWR_CLK_ENABLE();
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
HAL_Delay(5000);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
#if STOP_MODE_0
#elif STOP_MODE_1
__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI);
#elif STOP_MODE_2
#elif SLEEP_MODE_1
#elif STANDY_MODE_1
#elif SHUTDOWN_MODE_1
#elif LP_SLEEP_MODE_1
#elif LP_RUN_MODE_1
#endif
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/* Enable GPIOs clock */
#if !RUN_MODE || !LP_RUN_MODE
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_ALL;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* Disable GPIOs clock */
__HAL_RCC_GPIOA_CLK_DISABLE();
/* Suspend Tick increment to prevent wakeup by Systick interrupt. */
/* Otherwise the Systick interrupt will wake up the device within 1ms */
/* (HAL time base). */
HAL_SuspendTick();
#endif
#if STOP_MODE_0
HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI);
#elif STOP_MODE_1
HAL_PWREx_EnterSTOP1Mode(PWR_STOPENTRY_WFI);
#elif STOP_MODE_2
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
#elif SLEEP_MODE
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI);
#elif STANDY_MODE
HAL_PWR_EnterSTANDBYMode();
#elif SHUTDOWN_MODE
HAL_PWR_EnterSHUTDOWNMode();
#elif LP_SLEEP_MODE
HAL_PWR_EnterSLEEPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
#elif LP_RUN_MODE
/* Enter LP RUN Mode */
HAL_PWREx_EnableLowPowerRunMode();
#endif
/* Re-configure the system clock to 48 MHz based on MSI, enable and
select PLL as system clock source (PLL is disabled in STOP mode) */
#if LP_RUN_MODE
/* Disable low power run mode and reset the clock to initialization configuration */
//HAL_PWREx_DisableLowPowerRunMode();
#endif
#if RUN_MODE || LP_RUN_MODE
#else
SYSCLKConfig_WAKEUP();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
HAL_Delay(5000);
#endif
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE2);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_5;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/**
* @brief Configures system clock after wake-up from STOP: enable MSI, PLL
* and select PLL as system clock source.
* @param None
* @retval None
*/
static void SYSCLKConfig_WAKEUP(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
uint32_t pFLatency = 0;
/* Enable Power Control clock */
__HAL_RCC_PWR_CLK_ENABLE();
/* Get the Oscillators configuration according to the internal RCC registers */
HAL_RCC_GetOscConfig(&RCC_OscInitStruct);
/* After wake-up from STOP reconfigure the system clock: Enable HSI and PLL */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_5;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/* Get the Clocks configuration according to the internal RCC registers */
HAL_RCC_GetClockConfig(&RCC_ClkInitStruct, &pFLatency);
/* Select MSI as system clock source and configure the HCLK, PCLK1 and PCLK2
clocks dividers */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, pFLatency) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
2、测试结果
运行模式
0.5
低功耗运行模式
0.4
休眠模式
0.3
低功耗休眠模式
0.3
停止模式0
0.3
停止模式1
0.3
停止模式
0.3
待机模式
0.0018
关断模式
0.0017
整体来看,功耗是非常低了,在运行模式功耗低至0.5mA;在停止模式300uA.这功耗最适合做低功耗产品了。
测试数据:
运行模式
低功耗运行模式
停止模式、睡眠模式
待机模式
关断模式
- 2025-02-10
-
回复了主题帖:
【NUCLEO-U083RE开发板测评】03 低功耗串口测试
秦天qintian0303 发表于 2025-2-9 08:42
低功耗窗口在进入低功耗模式的时候还能接收到数据吗?
可以的
- 2025-02-08
-
发表了主题帖:
【NUCLEO-U083RE开发板测评】03 低功耗串口测试
本帖最后由 怀揣少年梦 于 2025-2-8 16:37 编辑
本次测评主要测试一下低功耗串口打印功能、功耗功能。
一、低功耗串口
1、介绍
能够在STOP MODE下,还能正常运行串口,此时的串口是限制了一些功耗的。并且还可以在STOP mode下进行数据传输
2、使用场景
主要在极低功耗要求的场景下使用
3、特点
带FIFO;
STOP mode可以唤醒;
可以调试,并能够进行RS485接收
二、实操
1、配置时钟
2、使能LPUART1
3、编写串口打印test代码
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int _io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__*/
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
PUTCHAR_PROTOTYPE {
uint8_t c=ch;
HAL_UART_Transmit(&hlpuart1, &c, 1, 100);
return ch;
}
#ifdef __GNUC__
__attribute__((weak)) int _write(int file, char *buf, int len) {
int idx = 0;
for(idx = 0; idx < len; idx++) {
_io_putchar(*buf++);
}
return len;
}
#else
__attribute__((weak)) int _write(int file, char *buf, int len) {
return 0;
}
#endif /* __GNUC__*/
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_LPUART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
printf("LPUART TEST\r\n");
HAL_Delay(1000);
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_5);
}
/* USER CODE END 3 */
}
实测打印
4、编写STOP MODE 低功耗串口唤醒测试代码
void EnterExitStopModeProcess(void)
{
/* Specify MSI as the clock source used after wake up from stop mode */
__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI);
/* wait for two seconds before test start */
HAL_Delay(2000);
/* make sure that no LPUART transfer is on-going */
while (__HAL_UART_GET_FLAG(&hlpuart1, USART_ISR_BUSY) == SET);
/* make sure that LPUART is ready to receive
* (test carried out again later in HAL_UARTEx_StopModeWakeUpSourceConfig) */
while (__HAL_UART_GET_FLAG(&hlpuart1, USART_ISR_REACK) == RESET);
/* set the wake-up event:
* specify wake-up on RXNE flag */
WakeUpSelection.WakeUpEvent = UART_WAKEUP_ON_READDATA_NONEMPTY;
if (HAL_UARTEx_StopModeWakeUpSourceConfig(&hlpuart1, WakeUpSelection) != HAL_OK)
{
Error_Handler();
}
/* Enable the UART Wake UP from STOP mode Interrupt */
__HAL_UART_ENABLE_IT(&hlpuart1, UART_IT_WUF);
HAL_SuspendTick();
printf("enter stop mode\r\n");
/* enable MCU wake-up by LPUART */
HAL_UARTEx_EnableStopMode(&hlpuart1);
/* ### Enter in Stop mode ########################################### */
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); // 进入停止模式之后,程序运行入口在这个地方
/* at that point, MCU has been awoken*/
SystemClock_Config_fromSTOP();
HAL_ResumeTick();
/* Wake Up on 4-bit address detection successful */
/* wait for some delay */
HAL_Delay(100);
printf("exit stop mode\r\n");
}
实测串口日志
5、实测功耗
1)进入停止模式后
唤醒之后的功耗
源码如下:
-
发表了主题帖:
【NUCLEO-U083RE开发板测评】02 RTC测试
本帖最后由 怀揣少年梦 于 2025-2-8 15:13 编辑
本篇测评开始测试STM32U083的RTC,主要使用RTC产生秒中断,持续观察一下RTC的时间精度
一、RTC介绍
1、特性
日历功能
拥有两个可编程闹钟
支持唤醒定时器
RTC还是和其他MCU一样,具备常用功能。
二、RTC如何配置
1、使能RTC
2、配置RTC时间,并屏蔽时分、日的闹钟
3、使能中断
4、编写代码
rtc.c
//设置闹钟
void SetRtcAlarm(void) {
/*The Alarm register can only be written when the corresponding Alarm is disabled (Use the HAL_RTC_DeactivateAlarm()).*/
RTC_TimeGet();
HAL_RTC_DeactivateAlarm(&hrtc, RTC_ALARM_A);//必须失能ALARM,才能进行写寄存器
sAlarm.AlarmTime.Seconds = sysTim.sec + 1;
if(sAlarm.AlarmTime.Seconds==60) {
sAlarm.AlarmTime.Seconds=0;
}
HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN);
}
//中断回调函数
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc) {
g_secFlag = 1;
SetRtcAlarm();
}
//获取时间
void RTC_TimeGet( void )
{
RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
sysTim.hour = sTime.Hours;
sysTim.min = sTime.Minutes;
sysTim.sec = sTime.Seconds;
sysTim.month = sDate.Month;
sysTim.day = sDate.Date;
sysTim.year = sDate.Year ;
}
//设置时间
void RTC_TimeSet( void )
{
RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;
sTime.Hours = sysTim.hour;
sTime.Minutes = sysTim.min;
sTime.Seconds = sysTim.sec;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
sDate.WeekDay = RTC_WEEKDAY_SATURDAY;
sDate.Month = sysTim.month ;
sDate.Date = sysTim.day ;
sDate.Year = sysTim.year ;
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
}
main.c
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
SystemPower_Config();
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_RTC_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if (g_secFlag) {
g_secFlag = 0;
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_5);
RTC_TimeGet();
printf("%d-%d-%d %d:%d:%d\r\n",2000 + sysTim.year,sysTim.month,sysTim.day,sysTim.hour,sysTim.min,sysTim.sec);
}
}
/* USER CODE END 3 */
}
三、下载验证
串口打印
1小时之后
从以上看,1小时RTC的精度相差了0.25s ,测试一天的时间精度相差在0.6s,精度还是相当不错的。
源码如下
-
回复了主题帖:
官方NUCLEO-U083RC开发板识别不到下载口
nmg 发表于 2025-2-8 14:04
我发现现在的数据线,坑挺多的,最让人想不到的是,手机官方带的线,竟然还不是数据线,只能充电,我过年 ...
那真是巨坑啊,官方带的线应该都是支持数据传输的
-
回复了主题帖:
这个春节最火爆的AI大模型deepseek,你玩了吗?
使用了一下,让其帮忙设计单片机最小系统,回答的还是比较准确的,就是不能绘制电路图
- 2025-02-06
-
回复了主题帖:
官方NUCLEO-U083RC开发板识别不到下载口
最终换了一根线就好了。
- 2025-01-28
-
回复了主题帖:
官方NUCLEO-U083RC开发板识别不到下载口
lugl4313820 发表于 2025-1-27 11:19
有几种情况,开发板上有时会有一个跳线,如果跳线没有接上,那MCU没有上电,他就识别不到。
还有就是有 ...
多谢大佬,检查了跳线,是没有问题的。数据线是新的,我换一根试试
- 2025-01-25
-
发表了主题帖:
官方NUCLEO-U083RC开发板识别不到下载口
背景:
基于NUCLEO-U083RC开发板 ,使用type-c的供电;
现象:
插到电脑上,ST-LINK驱动能识别到。
在KEIL里面识别不到下载口
测量电压电压5V,MCU的VDD电压也为3.3V,求大佬指教一二。
- 2025-01-24
-
回复了主题帖:
【NUCLEO-U083RE开发板测评】01 STM32U0魅力何在
秦天qintian0303 发表于 2025-1-24 10:33
低功耗是这个系列最主要的特点
是的,大佬。后续实际测试一下低功耗模式下的功耗
-
发表了主题帖:
【NUCLEO-U083RE开发板测评】01 STM32U0魅力何在
感谢EEWORLD给予这次测评的机会,也许久没有在论坛参加测评活动。也非常开心获得U083开发板的测评,曾在ST的公号里看到U0系列能够在太阳能板供电的情况进行数据传输以及正常工作,足见其功耗性能是很强的。那我们就先来看看U0系列有哪些吸引人的地方?
魅力之一:功耗
1、官方数据手册数据如下:
关断模式16nA;
待机模式:30nA;带RTC时,160nA;
停止模式2:695nA;带RTC时,有825nA;
由此可知,U083的功耗是真的低,竟然达到nA级别;
2、看看具体官方数据表
测试条件:所有外设失能,外部有源晶振;
1)运行模式和低功耗运行模式功耗如下
从上表可以看出
a、在2MHz运行模式功耗,不到250uA;且随着温度升高,功耗升高;
b、在2Mhz低功耗运行模式,与运行模式相差70uA左右;
2)停止模式功耗如下
停止模式0
停止模式1
停止模式2
从上表可以看出,功耗情况为 stop mode 0 > stop mode 1 > stop mode 2;整体都在uA级别;
3)待机模式
从上表可以看出,待机模式的功耗处于nA级别
4)关断模式
从上表可知,关断模式的功耗比待机模式的还要低;
魅力之二:外设
1、拥有丰富的外设
1)模拟外设(独立供电)
带PGA的运放和两个低功耗比较器
2)20个通信接口
串口就有7个,I2C有4个;
3)10个定时器
有3个可在停止模式运行的低功耗定时器
4)高达20个电容感应通道
魅力之三:安全
1、U0支持读保护;
2、硬件保护;
3、支持安全启动
4、支持AES加解密
从整体来看,U0系列适合低功耗应用,很强,外设丰富,可以用来做很多小东西。唯一遗憾的是,没有CAN,之后在选型的时候差点就用上这个U0了,但是没有CAN。
- 2025-01-19
-
回复了主题帖:
【测评入围名单(最后1批)】年终回炉:FPGA、AI、高性能MCU、书籍等65个测品邀你来~
个人信息无误,确认可以完成测评计划
- 2025-01-17
-
回复了主题帖:
【新年新挑战,任务打卡赢好礼!】第一批获奖名单公布
已确认
- 2025-01-16
-
回复了主题帖:
求推荐代码检查工具
吾妻思萌 发表于 2025-1-15 20:40
你是什么语言 怎么可能没有 Rust都有很好用的
C语言。之前用过CPPcheck,不是很好用
- 2025-01-15
-
回复了主题帖:
求推荐代码检查工具
wangerxian 发表于 2025-1-15 13:22
肯定有的,我觉得VSCode是目前市场上非常好用的编辑器了。
没找到合适的插件