STM32驱动QMC5883L
引脚定义
寄存器地址
00H
~ 05H
三轴地磁数据,小端模式
DRDY 新数据标识 1有新数据
OVL 溢出标志 寄存器的数值超过 int16_t
范围
TOUT 相对温度,每℃约变化100
00H
~ 05H
三轴地磁数据,小端模式
DRDY 新数据标识 1有新数据
OVL 溢出标志 寄存器的数值超过 int16_t
范围
TOUT 相对温度,每℃约变化100
|-----|-----|-----|
| 1 | 3 | 5 |
|-----|-----|-----|
| 8 | 7 | 2 |
|-----|-----|-----|
| 6 | 4 | |
|-----|-----|-----|
大概是这样一个 3*3
的棋盘,去掉右下角然后打乱,通过上下左右移动使之拼成正确的顺序
uint8_t board[COLS * ROWS] = { 0 };
/* 找到那个空位 */
uint32_t find_blank(void) {
for (int i = 0; i < COLS * ROWS; i++) {
if (board[i] == 0) {
break;
}
}
return i;
}
void init_board(void) {
/* 顺序填充面板,最后一个空着 */
for (int i = 0; i < COLS * ROWS - 1; i++) {
board[i] = i + 1;
}
/* Fisher-Yates 洗牌算法 */
for (int i = COLS * ROWS - 1; i > 0; i--) {
int j = rand() % (i + 1);
int temp = board[i];
board[i] = board[j];
board[j] = temp;
}
}
void move_up(void) {
uint32_t i = find_blank();
if (i < COLS * (ROWS - 1)) {
board[i] = board[i + COLS];
board[i + COLS] = 0;
return 1;
}
return 0;
}
void move_down(void) {
uint32_t i = find_blank();
if (i >= ROWS) {
board[i] = board[i - COLS];
board[i - COLS] = 0;
return 1;
}
return 0;
}
void move_left(void) {
uint32_t i = find_blank();
if (i % COLS < COLS - 1) {
board[i] = board[i + 1];
board[i + 1] = 0;
return 1;
}
return 0;
}
void move_right(void) {
uint32_t i = find_blank();
if (i % COLS > 0) {
board[i] = board[i - 1];
board[i - 1] = 0;
return 1;
}
return 0;
}
这个游戏只有胜利才是结束条件
int is_success(void) {
for (int i = 0; i < COLS * ROWS - 1; i++) { // 最后一个单元格不算
if (board[i] != i + 1) {
return 0;
}
}
return 1;
}
R1是下电阻,接 FB - GND
R2是上电阻,接 VOUT - FB
R3接 DAC - FB
#include <stdio.h>
#include <stdint.h>
float r1 = 10, r2 = 56, r3 = 12;
float vfb = 1.25;
float calc_vout(float vdac) {
return (1 + r2 / r1 + r2 / r3) * vfb - r2 / r3 * vdac;
}
int main(void) {
printf("R1 %.02f R, R1 %.02f R, R1 %.02f R, Vfb %.02f V\r\n", r1, r2, r3, vfb);
for (float i = 0; i <= 3.3; i += 0.3) {
printf("VDAC %.02f V; VOUT %.02f V\r\n", i, calc_vout(i));
}
return 0;
}
打印结果如下,可实现0-12V可调电源:
R1 10.00 R, R1 56.00 R, R1 12.00 R, Vfb 1.25 V
VDAC 0.00 V; VOUT 14.08 V
VDAC 0.30 V; VOUT 12.68 V
VDAC 0.60 V; VOUT 11.28 V
VDAC 0.90 V; VOUT 9.88 V
VDAC 1.20 V; VOUT 8.48 V
VDAC 1.50 V; VOUT 7.08 V
VDAC 1.80 V; VOUT 5.68 V
VDAC 2.10 V; VOUT 4.28 V
VDAC 2.40 V; VOUT 2.88 V
VDAC 2.70 V; VOUT 1.48 V
VDAC 3.00 V; VOUT 0.08 V
VDAC 3.30 V; VOUT -1.32 V
推导过程:
I1 = Vfb / R1
I2 = Vout - Vfb / R2
I3 = Vdac - Vfb / R3 # 电流方向可正可负,此处定义 Vdac -> Vfb 为正
I1 = I2 + I3
==> Vout = (1 + R2 / R1 + R2 / R3) * Vfb - R2 / R3 * Vdac
例如假设 0x12345678
存放在内存的 0x100
位置
则 0x100
地址存放的是 0x78
, 0x03
地址存放的是 0x12
即低位存放在低地址,高位存放在高地址
address: 0x100 0x101 0x102 0x103
value: 0x78 0x56 0x34 0x12
强制转换为 char
后,读取出来是 0x78
一般OLED的分辨率为128 * 64,使用 uint64_t
貌似可以大幅度提高性能
原理:
STM32是小端模式,也就是个位存放在低地址
例如对于 uint64_t = 0x1122334455667788ULL
在内存中的位置为:
0x11 0x22 0x33 ... 0x88
[0x00] [0x01] [0x02] ... [0x07]
把OLED的刷屏方向改成按列刷新
COL0 COL1 ......... COL127
PAGE0 [0x00] [0x08] ......... [0x0388]
PAGE1 [0x01] [0x09] ......... [0x0389]
................................
PAGE6 [0x06] [0x0E] ......... [0x038E]
PAGE7 [0x07] [0x0F] ......... [0x038F]
测试一下,代码如下:
#include <stdio.h>
#include <stdint.h>
int main(void) {
uint64_t a[2] = {
0x0011223344556677ULL,
0x8899AABBCCDDEEFFULL,
};
uint8_t* b = (uint8_t*)a;
for (int i = 0; i < 16; i++) {
printf("%d: 0x%02X\r\n", i, b[i]);
}
return 0;
}
运行结果:(这是在Windows里跑的,Windows也是小端)
0: 0x77
1: 0x66
2: 0x55
3: 0x44
4: 0x33
5: 0x22
6: 0x11
7: 0x00
8: 0xFF
9: 0xEE
10: 0xDD
11: 0xCC
12: 0xBB
13: 0xAA
14: 0x99
15: 0x88
--------------------------------
Process exited after 0.02992 seconds with return value 0 (0 ms cpu time, 2904 KB mem used).
Press ANY key to exit...
OLED 一般使用全屏缓冲区
#define OLED_WIDTH 128
#define OLED_HEIGHT 64
// uint8_t buf[OLED_WIDTH * OLED_HEIGHT / 8];
uint64_t buf[OLED_WIDTH];
#define MASK_SET(x, mask) buf[x] |= (mask);
#define MASK_CLR(x, mask) buf[x] &= ~ (mask);
/* 需要检测x,y在合理的范围内 */
void oled_draw_pixel(int x, int y, int col) {
if (x < 0 || x >= OLED_WIDTH || y < 0 || y >= OLED_HEIGHT) {
return;
}
if (col) {
MASK_SET(x, 1ULL << y); // ULL: 64位
} else {
MASK_CLR(x, 1ULL << y);
}
}
void oled_set_pixel(int x, int y) {
MASK_SET(x, 1ULL << y);
}
void oled_clr_pixel(int x, int y) {
MASK_CLR(x, 1ULL << y);
}
int oled_get_pixel(int x, int y) {
return (buf[x] >> y) & 0x01;
}
/* 超高性能 */
void oled_vline(int x, int y, int h, int col) {
uint64_t mask = ((1ULL << h) - 1ULL) << y;
if (col) {
MASK_SET(x, mask);
} else {
MASK_CLR(x, mask);
}
}
/* 也很快 */
void oled_hline(int x, int y, int w, int col) {
uint64_t mask = 1ULL << y;
if (col) {
while (w--) {
MASK_SET(x++, 1ULL << y);
};
} else {
while (w--) {
MASK_CLR(x++, 1ULL << y);
};
}
}
void oled_rect(int x, int y, int w, int h, int col) {
oled_vline(x, y, h, col);
oled_vline(x + w - 1, y, h, col);
oled_hline(x + 1, y, w - 2, col);
oled_hline(x + 1, y + h - 1, w - 2, col);
}
void draw_circle(int x0, int y0, int r, int col) {
int x = r;
int y = 0;
int err = 0;
while (x >= y) {
// 画圆上的8个对称点
set_pixel(x0 + x, y0 + y, col);
set_pixel(x0 + y, y0 + x, col);
set_pixel(x0 - y, y0 + x, col);
set_pixel(x0 - x, y0 + y, col);
set_pixel(x0 - x, y0 - y, col);
set_pixel(x0 - y, y0 - x, col);
set_pixel(x0 + y, y0 - x, col);
set_pixel(x0 + x, y0 - y, col);
// 更新y和误差
y += 1;
if (err <= 0) {
err += 2 * y + 1;
}
// 更新x和误差
if (err > 0) {
x -= 1;
err -= 2 * x + 1;
}
}
}