阿尔法开发板移植ov5640时候一个小的注意事项

问题:移植后摄像头显示异常:颜色不对,显示两个相同的区域,且只显示半个屏幕 解决方法:以我的4.3寸屏幕为例 一定要把bits-per-pixel改成16,因为ov5640不支持24等其他格式,所以导致字节写入和读取错误,最终导致显示错误,并不是摄像头驱动问题,而是显示驱动问题。 /* 4.3寸800*480 */ &lcdif { pinctrl-names = “default”; pinctrl-0 = <&pinctrl_lcdif_dat /* 使用到的IO */ &pinctrl_lcdif_ctrl>; display = <&display0>; status = “okay”; display0: display { /* LCD属性信息 */ bits-per-pixel = <16>; /* 一个像素占用24bit */ bus-width = <24>; /* 总线宽度 */ display-timings { native-mode = <&timing0>; /* 时序信息 */ timing0: timing0 { clock-frequency = <31000000>; /* LCD像素时钟,单位Hz,实际应该会被分成33000000,31M是理论的,而能实际分频的是33M*/ hactive = <800>; /* LCD X轴像素个数 */ vactive = <480>; /* LCD Y轴像素个数 */ hfront-porch = <40>; /* LCD hfp参数 */ ...

十月 2, 2025 · 1 分钟 · 142 字 · Me

正点原子-阿尔法开发板-第六十一章 Linux I2C驱动实验代码详解(自我理解)

1 #include<linux/types.h> 2 #include<linux/kernel.h> 3 #include<linux/delay.h> 4 #include<linux/ide.h> 5 #include<linux/init.h> 6 #include<linux/module.h> 7 #include<linux/errno.h> 8 #include<linux/gpio.h> 9 #include<linux/cdev.h> 10 #include<linux/device.h> 11 #include<linux/of_gpio.h> 12 #include<linux/semaphore.h> 13 #include<linux/timer.h> 14 #include<linux/i2c.h> 15 #include<asm/mach/map.h> 16 #include<asm/uaccess.h> 17 #include<asm/io.h> 18 #include"ap3216creg.h" 19 20 /* 这个驱动对应只有一个同类设备的问题,如果有多个同类型的传感器怎么处理,这就需要再学习了 */ 21 /* 一、配置设备相关的信息 */ 22 23 #define AP3216C_CNT 1 /* 定义设备数量 */ 24 #define AP3216C_NAME "ap3216c" /* 设备名称 */ 25 26 struct ap3216c_dev { 27 dev_t devid; /* 设备号,dev_t就是个普通的整数,是主设备号和次设备号计算出来的 */ 28 struct cdev cdev; /* cdev, 表示一个字符设备,需要向系统注册 */ 29 struct class *class; /* 类,表示一类设备,在/sys/class下,还理解的不深刻*/ 30 struct device *device; /* 创建的设备,在/dev/下显示的设备,通过device_create创建 */ 31 struct device_node *nd; /* 设备节点,存储设备树相关的信息,接收申请的返回值?本实验没有用到*/ 32 int major; /* 主设备号*/ 33 void *private_data; /* 私有数据,这个应该主要是自己用的,这里存储的是i2c_client,和filp->private_data要区分开 */ 34 unsigned short ir, als, ps; /* 传感器数据 */ 35 }; 36 37 static struct ap3216c_dev ap3216cdev; /* 全局变量,用于整体设备的控制 */ 38 /** 39 * @自我理解:封装读取寄存器这个函数,用于被后面调用,这里可以读取以reg为首地址的连续的多个值,跟 40 * 后面的ap3216c_read_reg读取单个寄存器的要区分开,这个ap3216没有用到(可能也不支持)读取连续地址对应的数值,这 41 * 个函数可能是为了编码规范而编写的,是一些通用的编程规范。 42 * @param - dev : ap2316c设备 43 * 44 */ 45 46 /* 二、一些自定义的读取写函数,这里的都不确定,因为基本都是自己定义的 */ 47 48 static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len) 49 { 50 int ret; /* 保存返回值 */ 51 struct i2c_msg msg[2]; /* i2c发送的数据,这里需要写入,然后读取,所以需要两个就行了。msg可以理解为数据包 */ 52 struct i2c_client *client = (struct i2c_client*)dev->private_data; /* 从自定义的设备结构体中取出i2c_client这个重要的操作对象 */ 53 /**也就是需要先向芯片发送写命令,然后发送要读取的寄存器的地址(相当于把寄存器地址写入芯片), 54 * 然后再发送读命令,这样芯片返回的数据就是刚才写入的这个寄存器地址的对应的值了? 55 * 这里的好几个读和写的含义要区分开,一个是对于i2c来说的,一个是对于寄存器来说。 56 * */ 57 /* msg[0]为一个写命令数据包:为发送要读取的寄存器的首地址 */ 58 msg[0].addr = client->addr; /* ap3216c地址 */ 59 msg[0].flags = 0; /* 标记为发送数据,就是i2c协议中的那个读写标志位 */ 60 msg[0].buf = &reg; /* 向芯片发送(写入)要读取的寄存器的地址 */ 61 msg[0].len = 1; /* 要发送的数据长度,也就是寄存器地址长度,以字节为单位 */ 62 63 /* msg[1]为一个读命令数据包:读取msg[0]中发送的寄存器地址的值 */ 64 msg[1].addr = client->addr; /* ap3216c地址 */ 65 msg[1].flags = I2C_M_RD; /* 标记为发送数据,就是i2c协议中的那个读写标志位 */ 66 msg[1].buf = &reg; /* 向芯片发送(写入)要读取的寄存器的地址 */ 67 msg[1].len = 1; /* 要发送的数据长度,也就是寄存器地址长度,以字节为单位 */ 68 69 ret = i2c_transfer(client->adapter, msg, 2); /* 发送数据,这里client->adapter参数不能省略,因为一个i2c口对应一个适配器,也就是说一个设备如果有多个i2c口,就会有多个适配器,所以需要制指定从那个i2c口发,这里msg是一个数组,所以是一个地址(严格意义上不算地址)*/ 70 if(ret == 2) { 71 ret = 0; // 发送成功 72 } else { 73 printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len); 74 ret = -EREMOTEIO; /* EREMOTEIO = 121 */ 75 } 76 return ret; 77 } 78 79 /** 80 * 向寄存器写入数据,这里也是可以写入连续的数据,跟ap3216c_write_reg区分开,解释见上 81 * 面的ap3216c_read_regs注释 82 */ 83 static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len) 84 { 85 u8 b[256]; /* 申请256字节栈空间 */ 86 struct i2c_msg msg; /* 要写入数据的数据包 */ 87 struct i2c_client *client = (struct i2c_client *)dev->private_data; 88 89 90 /* 要发送的数据b,在主机发送写命令后,ap3216c要求先发送要写入的寄存器地址,然后再发送要写入的数据 */ 91 b[0] = reg; /* 要写入数据的寄存器首地址 */ 92 memcpy(&b[1], buf, len); /* b[1]为目标,buf为源,len长度,b[0]已经存放了寄存器地址,从b[1]开始存储要写入的数据 */ 93 94 msg.addr = client->addr; /* 地址 */ 95 msg.flags = 0; /* 写数据 */ 96 97 msg.buf = b; /* 要写入的数据缓冲区 */ 98 msg.len = len + 1; /* 要写入的数据的长度,还有个寄存器地址 */ 99 100 return i2c_transfer(client->adapter, &msg, 1); /* 这里msg是一个实体,所以需要取地址 */ 101 } 102 103 /** 104 * 只读取一个字节,所以用了unsigned char 105 */ 106 static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg) 107 { 108 u8 data = 0; 109 ap3216c_read_regs(dev, reg, &data, 1); /* 长度为1,所以只读取一个寄存器 */ 110 return data; 111 112 #if 0 /* 这里也是一个知识点,i2c_smbus_read_byte_data函数是对i2c_transfer跟高级别的封装,可能需要开启某些选项才行 */ 113 struct i2c_client *client = (struct i2c_client *)dev->private_data; 114 return i2c_smbus_read_byte_data(client, reg); 115 #endif 116 } 117 118 /** 119 * 具体关系和解释同上面的读函数 120 */ 121 static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data) 122 { 123 u8 buf = 0; 124 buf = data; 125 ap3216c_write_regs(dev, reg, &buf, 1); 126 } 127 128 /** 129 * 利用上面的函数,对上面函数进行封装,读取原始数据,如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms 130 * 发现这个ap3216c_dev结构体贯穿时钟啊,看来很重要呢 131 */ 132 void ap3216c_readdata(struct ap3216c_dev *dev) 133 { 134 unsigned char i =0; /* 可能尽量节省空间吧 */ 135 unsigned char buf [6]; /* 节省空间 */ 136 137 for(i = 0; i < 6; i++){ 138 buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + 1); 139 } 140 141 if(buf[0] & 0X80){ /* 看数据手册可知其第八位为数据是否有效标志位 */ 142 dev->ir = 0; /* 无效的话,设置为0 */ 143 } else { 144 /* buf[0]低两位 + buf[1] */ 145 dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03); /* unsigned short 等价于 unsigned short int 占用2字节,是int的一半?也是为了节省空间*/ 146 } 147 148 dev->als = ((unsigned short)buf[3] << 8) | buf[2]; /* 数据计算同理 */ 149 if(buf[4] & 0x40){ /* 标志位,为1则数据无效 */ 150 dev->ps = 0; 151 } else { 152 dev->ps = ((unsigned short)(buf[5] & 0x3F) << 4) | (buf[4] & 0x0F); 153 } 154 } 155 156 157 /* 三、用户层面文件操作相关的接口*/ 158 159 /** 160 * 被用户打开时调用的函数,跟prob函数分开,那个是与设备树匹配成功后执行的函数 161 * @param-inode: 内核用来保存文件元信息,比如文件类型、权限、大小、时间戳、设备号等,以供驱动可以判断一些东西 162 * 只有打开的时候会传递inode,然后内核会自动将file结构体中的f_inode赋值为inode。 163 */ 164 static int ap3616_open(struct inode *inode, struct file *filp) 165 { 166 filp->private_data = &ap3216cdev; /* 注意这里的private_data跟自定义的private_data不一样,这个是linux编程指定的,不是自定义 */ 167 /* 初始化ap3216c, 正常来说这些应该都要加判断是否成功的吧 */ 168 ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x04); /* 0x40是软复位 */ 169 mdelay(50); /* 延迟50ms,因为复位需要实践 */ 170 ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x03); /* 使能ALS+PS+IR */ 171 return 0; 172 } 173 /* 174 * @description : 从设备读取数据 175 * 注意:这里不需要传递inode了,因为在filp里面已经有了 176 * @param - filp : 要打开的设备文件(文件描述符) 177 * @param - buf : 返回给用户空间的数据缓冲区 178 * @param - cnt : 要读取的数据长度 179 * @param - offt : 相对于文件首地址的偏移 180 * @return : 读取的字节数,如果为负值,表示读取失败 181 */ 182 static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off) 183 { 184 short data[3]; /* 保存数据 */ 185 long err = 0; /* 返回值,可以用ret吧? */ 186 187 struct ap3216c_dev *dev = (struct ap3216c_dev*)filp->private_data; 188 189 ap3216c_readdata(dev); 190 191 data[0] = dev->ir; 192 data[1] = dev->als; 193 data[2] = dev->ps; 194 err = copy_to_user(buf, data, sizeof(data)); 195 return 0; 196 } 197 /** 198 * 用不到write,但是加上吧,了解下函数结构 199 * @param – filp : 设备文件,表示打开的文件描述符 200 * 注意:这里不需要传递inode了,因为在filp里面已经有了 201 * @param - buf : 要写给设备写入的数据 202 * @param - cnt : 要写入的数据长度 203 * @param – offt : 相对于文件首地址的偏移 204 */ 205 static ssize_t ap3216c_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) 206 { 207 return 0; 208 } 209 /** 210 * 用户层面文件关闭/释放设备时候调用的函数,跟后面驱动相关的exit/remove函数区分开 211 */ 212 static int ap3216_release(struct inode *inode, struct file *filp) 213 { 214 return 0; 215 } 216 217 /** 218 * 构建AP3216用户文件操作层面的结构体函数,注意,这里是文件操作 219 */ 220 static const struct file_operations ap3216c_ops = { 221 .owner = THIS_MODULE, 222 .open = ap3616_open, 223 .read = ap3216c_read, 224 .write = ap3216c_write, 225 .release = ap3216_release, 226 }; 227 228 /* 到此未知,有关用户层面对文件的打开关闭操作已经有了 */ 229 230 /* 四、下面是驱动层面干的事情了,就是对设备相关的操作了,更加底层 */ 231 232 /** 233 * prob函数,最重要的函数,用于设配匹配成功后执行 234 * 要记住不同总线的prob函数应该是不一样的 235 * 这里的这两个参数应该是从设备树或者传统匹配方式得到的数据,设备匹配成功后会把这两个参数传递进来 236 */ 237 static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id) 238 { 239 /* 1. 构建设备号,AP3216C_CNT>1的情况我还没懂,但大体意思就是一个驱动管理多个同类型的传感器,可以公用一套驱动代码,等待研究*/ 240 if(ap3216cdev.major){ /* 存在设备号,也就是有自己想设置的设号 */ 241 ap3216cdev.devid = MKDEV(ap3216cdev.major, 0); /* 设置主设别号和次设备号,次设备号一般设置为0 */ 242 register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME); 243 } else { 244 alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME); /* 得到设备号,可以解析出主设备号和次设备号,参数0代表的是启示开始分配的设备号,一般从0开始,也就是说如果从0开始,就是从0开始一个个查找,查找到可用的就用上,也对应上一个驱动适配多个相同设备了 */ 245 ap3216cdev.major = MAJOR(ap3216cdev.devid); /* 解析出主设备号 */ 246 } 247 248 /* 2. 注册字符设备,向内核中注册一个字符设备,但是用户层面还是看不到相应的设备 */ 249 cdev_init(&ap3216cdev.cdev, &ap3216c_ops); /* 第一个参数是要初始化的设别结构体,第二个是文件操作函数 */ 250 cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT); /* 第三个参数是要创建的设备数量 */ 251 252 /* 3. 创建类,就是在/sys/class/创建同一类设备的文件夹 */ 253 ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME); 254 if( IS_ERR(ap3216cdev.class)){ /* 如果创建类是黑白失败 */ 255 return PTR_ERR(ap3216cdev.class); 256 } 257 258 /* 4. 创建设备,使其暴露在/dev/下面,让用户使用 */ 259 /** 260 * device_create是个可变参数函数,参数 class就是设备要创建哪个类下面;参数 parent是父 261 * 设备,一般为 NULL,也就是没有父设备;参数 devt是设备号;参数 drvdata是设备可能会使用 262 * 的一些数据,一般为 NULL;参数 fmt是设备名字,如果设置 fmt=xxx的话,就会生成 /dev/xxx这个设备文件。返回值就是创建好的设备。 263 */ 264 ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL, AP3216C_NAME); 265 if(IS_ERR(ap3216cdev.device)){ 266 return PTR_ERR(ap3216cdev.device); 267 } 268 ap3216cdev.private_data = client; 269 270 return 0; 271 } 272 /** 273 * 设备移除时对设备进行的操作,可能类似设备拔出之后的操作 274 */ 275 static int ap3216c_remove(struct i2c_client *client) 276 { 277 /*1. 删除字符设备相关 */ 278 cdev_del(&ap3216cdev.cdev); 279 /* 这个注销函数,应该是从devid开始的设备号,忘后AP3216C_CNT注销AP3216C_CNT个设备,因为前面注册的时候,是对应AP3216C_CNT个设备的 */ 280 /* 这些函数的详细用法还需要单独学习,可以看官方手册? */ 281 unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT); /* 这里AP3216C_CNT是1,也就是说值对应一个设备的情况,多个设备的情况还需要再学习 */ 282 283 /* 2. 注销掉类和设备 */ 284 device_destroy(ap3216cdev.class, ap3216cdev.devid); 285 class_destroy(ap3216cdev.class); 286 return 0; 287 } 288 289 /* 除了设备树之外的匹配方式都叫传统方式 */ 290 /* 传统匹配方式 */ 291 static const struct i2c_device_id ap3216c_id[] = { 292 {"alientek,ap3216c", 0}, /* 注意这里的0不是什么ID,而是一个私有数据,可能是用于区分不同的设备? */ 293 {} 294 }; 295 296 /* 设备树匹配方式 */ 297 static const struct of_device_id ap3216c_of_match[] = { 298 { .compatible = "alientek,ap3216c"}, 299 { } 300 }; 301 302 /* 写完相关函数之后,下面配置驱动相关的操作的结构体 */ 303 /** 304 * 感觉这个才是真正封装好的驱动,其中有驱动的各种操作,所以init中是将这个add进去的 305 */ 306 static struct i2c_driver ap3216c_driver = { 307 .probe = ap3216c_probe, 308 .remove = ap3216c_remove, 309 .driver = { 310 .owner = THIS_MODULE, 311 .name = "ap3216c", 312 .of_match_table = ap3216c_of_match, 313 }, 314 .id_table = ap3216c_id, 315 }; 316 317 /* 驱动入口函数,这个是最先执行的函数 */ 318 319 static int __init ap3216c_init(void) 320 { 321 int ret = 0; 322 ret = i2c_add_driver(&ap3216c_driver); 323 return ret; 324 } 325 /** 326 * 驱动出口函数,没什么好说的,卸载模块的时候用 327 */ 328 static void __exit ap3216c_exit(void) 329 { 330 i2c_del_driver(&ap3216c_driver); 331 } 332 333 /* module_i2c_driver(ap3216c_driver) */ /* 这个是直接把module_init和module_exit简化成要给函数了 */ 334 module_init(ap3216c_init); 335 module_exit(ap3216c_exit); 336 MODULE_LICENSE("GPL"); 337 MODULE_AUTHOR("ydteng");

九月 24, 2025 · 7 分钟 · 1346 字 · Me

正点原子linux阿尔法开发板4.3寸触摸不生效问题

解决问题的历程 刚开始直接加载驱动会提示MX6UL_PAD_GPIO1_IO09已经被占用。我是直接编译进内核中,使用命令dmesg | grep -i tsc查看的,如果编译成ko文件再加载应该也是这种报错。 imx6ul-pinctrl 2290000.iomuxc-snvs: no groups defined in /soc/aips-bus@02200000/iomuxc-snvs@02290000/tsc_reset imx6ul-pinctrl 20e0000.iomuxc: pin MX6UL_PAD_GPIO1_IO09 already requested by 20e0000.iomuxc; cannot claim for 2040000.tsc 于是直接搜索设备树,发现如下节点(原来不是注释的),是冲突的,我直接注释 (为了能方便看到输出log,从直接加载进内核,我改成手动加载.ko文件了)加载之后不报错了,但是还是不行 又网上找说新的触摸芯片不是gt9147了,是gt1151,按照《4.3寸的800*480的屏,GT9147驱动,触摸屏没反应-OpenEdv-开源电子网》这里提供的一份文档,修改了还是不行,实际上现在正点原子的教程和源码都是兼容这两款的了 于是我顺着找到《关于I.MX6U 4.3寸RGB LCD触摸屏设备树修改的问题。-OpenEdv-开源电子网》这篇文章,需要注释掉xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;这段,因为这段也是占用可GPIO1_IO9这个引脚。 于是就可以了,也不用按照文档《4.3寸的800*480的屏,GT9147驱动,触摸屏没反应-OpenEdv-开源电子网》改任何地方。 小白刚入门,至于为什么这个地方也能占用引脚,我还没搞懂,等待慢慢学习。 正点原子教程中,每次设备树修改完都让检查有没有重复的,这次算是知道了“xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;”这种方式也能占用,以后一定要检查好!!! 更新:这应该不算是GPIO冲突,而是一种重复配置同一功能导致的冲突?

九月 6, 2025 · 1 分钟 · 43 字 · Me

Pycharm不自动激活conda环境的问题

不知道什么原因(怀疑是有哪个软件更新了),Pycharm不自动激活conda环境,按照网上的各种教程都不行 解决办法:不要设置Anaconda的那些环境变量就行了(或者重装Anaconda) 并且Anaconda官方也建议不要设置这些环境变量,如果为了使用方便可以单独安装一个独立的python,看个人习惯吧。我是暂时先使用Anaconda的Anaconda Prompt命令行

九月 2, 2025 · 1 分钟 · 4 字 · Me

新版VSCode通过SSH远程连接无法连接Ubuntu旧版的方法

首先参考:最新版VSCode通过SSH远程连接Ubuntu 16.04等旧版Linux的方法_我可以在较旧的 linux 发行版上运行 vs code server 吗?-CSDN博客 注意其中最重要的最后一步:一定把环境变量放到.bashrc的最开头! 最开头! 最开头! 执行过程可能会遇到问题,无法下载zlib,可以参照(39 封私信 / 81 条消息) ct-ng_下载源码和编译_ERROR-zlib-download-failed - 知乎 将crosstool-ng-1.26.0.tar.bz2换成crosstool-ng-1.27.0.tar.bz2(26换成27)

六月 10, 2025 · 1 分钟 · 20 字 · Me

Clion+Platformio配置stm32一些坑

主要问题在于三个文件core_cm3.c、startup_xxx.s、system_stm32f10x.c,因为这些文件PIO中都有默认文件,容易产生冲突 对于core_cm3.c,可以参考按照https://blog.csdn.net/guangod/article/details/96427017修改cotex_m3的代码之后可以解决汇编问题,也可以直接删除,使用pio默认的 对于遇到的问题,都可以删除自己导入的core_cm3.c、startup_xxx.s、system_stm32f10x.c三个文件解决 使用platformio开发stm32标准库 - 无 忧里面说了可以删除pio自带的来使用自己的文件,但是我觉得修改默认的东西不优雅,目前还没找到一个好的方法,在保留默认文件的情况下使用自己的文件。

五月 30, 2025 · 1 分钟 · 7 字 · Me

FreeRTOS知识随笔

FreeRTOS知识随笔 1. 中断函数SysTick_Handler,用于产生FreeRTOS的tick port.c中 portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;为设置SysTick_Handler的中断周期 其中portNVIC_SYSTICK_LOAD_REG 是一个寄存器,值为( *( ( volatile uint32_t * ) 0xe000e014 ) ),决定计数器从该值递减到 0 的计数周期长度。 configSYSTICK_CLOCK_HZ和configTICK_RATE_HZ 都可以在FreeRTOSConfig中配置 例:configSYSTICK_CLOCK_HZ 为72MHZ(72000000HZ),configTICK_RATE_HZ 设置为1000的话,下面的计算方法比较繁琐,但是便于理解。 1/72000000 = 每个时钟周期的频率,以秒为单位;72000000/1000 = 72000表示产生一次SysTick_Handler中断的时钟周期,也就是72000*(1/72000000)= 0.001s,也就是1ms。最终得到1ms得到一个tick。 注:vTaskDelay()的参数也是以tick为单位的。 2. 为什么设置flash延时 Flash 延时周期 是指 CPU 从内部 Flash 读取指令或数据时,为了配合高频率主频运行,需要插入的一定数量的延迟周期(Wait States),确保读取数据的正确性。(这个需要再看一下) 3. FreeRTOS 确实以 Tick 为周期性心跳来驱动调度,但任务切换(上下文切换)的最小触发单位可以比 Tick 更小,因为它可以在任何需要的时候被其他事件(如中断、任务阻塞等)立即触发。 4. 在不同的系统中,一个字WORD占据的空间不同。 1、在32位嵌入式系统中,一个字WORD占32bit,即4个字节; 2、半字Half-word占16bit; 3、字节Byte占8位。 5. FreeRTOS 任务优先级越大越高,stm32中断优先级越大越低 6. 与lvlg同时使用的时候,一定要配置好合适的configTOTAL_HEAP_SIZE(在FreeRTOSConfig里)

五月 30, 2025 · 1 分钟 · 65 字 · Me

stm32f103ZET6移植LVGL的一些要点

stm32f103ZET6移植LVGL的一些要点 内存不足,报错:No space in execution regions with .ANY selector matching … 修改lv_config下的#define LV_MEM_SIZE (16 * 1024U)中的16修改为合适的大小 参考:https://blog.csdn.net/m0_58418074/article/details/143819363 可以找出大小的占用情况 使用的是正点原子的精英开发(带屏幕版本) 注意下面的情况,一定要包含,要不lcd的延迟函数会卡住死循环 sys_stm32_clock_init(9); delay_init(72); // 必须要有 启动文件堆栈要修改大小,下图中的8k是能运行的 还会报一大堆警告,可以先不管

五月 30, 2025 · 1 分钟 · 26 字 · Me

IntelliJ IDEA新建SpringBoot项目

IntelliJ IDEA新建SpringBoot项目 前言 虽然新建项目比较简单,但还是有几个点需要注意。 步骤 下载和安装IDEA不再介绍 新建工程 点击“New Project” 标红的为重点关注 需要关注的几个字段: Name:项目/模块名 Artifact:相当于具体的功能名 Group:可以理解为分组,例如属于那个项目组 Package Name:包名,可以长点,也可以短点,例如:com.example.demo或者com.example 注意java8要选择springboot版本要低于3,依赖自己按需选择,这里只选择了spring web 刚创建完工程后,IDEA还未识别出maven工程,需要等待其初始化完成(初始化可能会遇到网络问题,可自行换源) 初始化完成后,对比右侧栏,如何迟迟没有变化,可能是maven的配置问题。 有的可能没有绿色的高亮,这是IDEA的git自动检测,不用在意。 开始学习或者开发 结语 注意几个要点即可,特别是maven项目加载问题、java和springboot版本的对应问题。

九月 8, 2023 · 1 分钟 · 24 字 · Me

SpringBoot整合mybatis(plus)单表查询和多表查询

SpringBoot整合mybatis(plus)单表查询和多表查询 前言 mybatis是springboot常用的操作数据库的框架,能够大大简化数据库操作,其可以进行xml配置开发,也可以进行注解开发。虽然现在有mybatis-plus,功能很强大,但也只是简化了单表操作,多表操作甚是麻烦。小型项目怎么用都可以,但是对于大型项目来说还是推荐mybatisXML进行多表操作,用mybatis-plus进行单表操作的组合。注解开发感觉位于这两个中间,显得有点尴尬,推荐用于小型项目。 单表查询(用mybatis-plus自带) 使用mybatis-plus进行单表查询较为简单,只需继承自带的类即可,举例如下: //Dao层 public interface UserDao extends BaseMapper<UserInfo> { } //service层 public interface UserService extends IService<UserInfo> { } 其他详情可以看mybatis-plus官网 多表查询(XML配置) 重点在多表查询查询,下面是用单表查询的例子,要想多表查询只需更改sql语句即可。 导入包 这里我用的是mybatis-plus,其完全支持原生mybatis的配置开发,还能进行方便的单表操作。 将下面代码加入pom文件中,注意版本不一定非得一样。 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency> 修改配置文件 我这里是配置文件是:application.yml 因为是整合进springboot,这里不需要配置官网所说的mybatis-config.xml文件,只需配置下面的文件就可以。 #配置数据库 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test username: root password: root #配置mybatis-plus插件 mybatis-plus: global-config: db-config: id-type: auto #用于id自增 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #日志类型 type-aliases-package: com.test.domain #配置实体类位置,这样以后就不用写完整的路径名了 mapper-locations: classpath:/mappers/*.xml #较为重要,配置mapper的xml文件位置。 添加mapper的xml文件,放到resource下的mappers目录中 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.test.dao.UserDao"> <select id="selectAll" resultType="UserInfo"> select * from user_info </select> </mapper> 编写实体类 这里只是个例子:UserInfo.java ...

七月 14, 2023 · 2 分钟 · 220 字 · Me