RS485和RS232 通信配置
RS232
目前硬件上支持RS232的有以下板卡:
-
LubanCat-5IO底板(含有RS232x2)
7.1. 引脚定义
具体的引脚定义可以参考背面的丝印
-
LubanCat-5IO底板
引脚定义图
7.2. 跳帽配置
-
LubanCat-5IO底板
鲁班买5IO底板上的RS485和RS232是共用同一组UART信号的,使用跳帽切换RS485或者RS232
7.3. 软件配置
232引脚 | 对应串口 |
RS232-1 | UART4_M2 |
RS232-2 | UART7_M2 |
这里以开启RS232-1为例,开启RS232-1即开启UART4_M2设备树插件,相应的开启RS232-2即开启UART7_M2设备树插件
-
修改配置文件
1 2 | #修改配置文件 sudo vi /boot/uEnv/uEnv.txt |
7.3.1. 开启设备树插件
这里以打开RS232-1为例
-
把
#dtoverlay=/dtb/overlay/rk3588-lubancat-uart4-m2-overlay.dtbo
前面的#删除,如下图
7.3.2. 关闭设备树插件
这里以关闭RS232-1为例
-
在
dtoverlay=/dtb/overlay/rk3588-lubancat-uart4-m2-overlay.dtbo
前面添加#,如下图
修改完配置文件后需要重启生效
1 2 | #重启 sudo reboot |
7.4. 使用方法
这里以RS232-1为例
7.4.1. 查询串口4的通信参数
串口4外设使能后,在/dev目录下生成ttyS4设备文件,用stty工具查询其通信参数
1 2 3 4 | #需要在root用户下运行#在板卡的终端执行如下命令 stty -F /dev/ttyS4 |
如下图:
7.4.2. 修改串口波特率
1 2 3 4 | #需要在root用户下运行#设置通讯速率,其中ispeed为输入速率,ospeed为输出速率 stty -F /dev/ttyS4 ispeed 115200 ospeed 115200 |
如下图:
7.4.3. 关闭回显
默认串口是开启回显的 可以使用以下命令关闭回显
1 2 3 | #需要在root用户下运行stty -F /dev/ttyS4 -echo |
7.4.4. 通讯
1 2 3 4 5 6 7 | #需要在root用户下运行#发送消息 echo "I'm lubancat" > /dev/ttyS4#接收消息 cat /dev/ttyS4 |
除了这种方法外,还可以使用minicom,具体方法可以参考下列文章 串口章节
7.5. 自检测试
该测试需要把RS232引脚的相连接
TX1 | 连接 | RX2 |
RX1 | 连接 | TX2 |
然后需要同时打开RS232-1和RS232-2的设备树插件。
1 2 3 4 5 6 7 8 | #需要在root用户下运行#RS232-2后台接收 cat /dev/ttyS7 &#RS232-1发送消息 echo "1234567890" > /dev/ttyS4 echo "abcdefghijklmn" > /dev/ttyS4 |
发送完成就可以看到有接收到消息了,如下图所示
如果想结束后台运行的 cat /dev/ttyS7 &
,参照以下操作
1 | pkill cat |
RS232-1接收&RS232-2发送的方法与上述方法类似,可以自行修改操作。
RS485
目前硬件上支持RS485的有以下板卡:
-
LubanCat-5IO底板(含有RS485x2)
8.1. 引脚定义
具体的引脚定义可以参考背面的丝印
-
LubanCat-5IO底板
引脚定义图
8.2. 跳帽配置
-
LubanCat-5IO底板
鲁班买5IO底板上的RS485和RS232是共用同一组UART信号的,使用跳帽切换RS485或者RS232
8.3. 对应串口
LubanCat-5IO底板RS485对应UART接口如下:
485引脚 | 对应串口 |
RS485-1 | UART4_M2 |
RS485-2 | UART7-M2 |
8.3.1. 软件配置
重要
野火2024年9月12日支持RS485驱动层换流,不需要再在应用层手动换流,使用方法和普通串口一样,十分简单,因此建议使用新镜像或更新内核,使用驱动层换流。如果比2024年9月12日旧的镜像可以更新内核或者直接使用新镜像。
8.4. 使用驱动层换流
这里以开启RS485-1为例,开启和关闭,rs485对应的 rs485插件 ,支持RS485的板卡操作相同,只是设备树插件名字稍有差异。
1 2 | #修改配置文件 sudo vi /boot/uEnv/uEnv.txt |
这里以LubanCat-5IO底板 开启RS485-1 为例
-
把
#dtoverlay=/dtb/overlay/rk3588-lubancat-5io-rs485-1-overlay.dtbo
前面的#删除,如下图
这里以LubanCat-5IO底板 关闭RS485-1 为例
-
在
#dtoverlay=/dtb/overlay/rk3588-lubancat-5io-rs485-1-overlay.dtbo
前面添加#,如下图
修改完配置文件后需要重启生效
1 2 | #重启 sudo reboot |
8.5. 使用应用层换流
这里以开启LubanCat-5IO底板RS485-1为例,开启和关闭rs485对应的 串口插件 ,支持RS485的板卡操作相同,只是设备树插件名字稍有差异,需要根据实际对应的串口进行修改。
1 2 | #修改配置文件 sudo vi /boot/uEnv/uEnv.txt |
这里以LubanCat-5IO底板为例
-
把
#dtoverlay=/dtb/overlay/rk3588-lubancat-uart4-m2-overlay.dtbo
前面的#删除,如下图
这里以LubanCat-5IO底板为例
-
在
#dtoverlay=/dtb/overlay/rk3588-lubancat-uart4-m2-overlay.dtbo
前面添加#,如下图
修改完配置文件后需要重启生效
1 2 | #重启 sudo reboot |
8.6. 使用方法
重要
使用驱动层换流不需要再在应用层手动换流,使用应用层换流则需要在应用层手动控制流控引脚电平实现换流。
这里以LubanCat-5IO底板RS485-1为例,该底板RS485-1对应串口是UART4-M2。
LubanCat-5IO底板RS485-1对应的设备树插件使能后,在/dev目录下生成ttyS4设备文件,用stty工具查询其通信参数
1 2 3 4 5 6 7 8 | #需要在root用户下运行#在板卡的终端执行如下命令 stty -F /dev/ttyS4#信息输出如下 speed 9600 baud; line = 0; -brkint -imaxbel |
1 2 3 4 | #需要在root用户下运行#设置通讯速率,其中ispeed为输入速率,ospeed为输出速率 stty -F /dev/ttyS4 ispeed 115200 ospeed 115200 |
默认串口是开启回显的 可以使用以下命令关闭回显
1 2 3 | #需要在root用户下运行stty -F /dev/ttyS4 -echo |
1 2 3 4 5 6 7 8 | #需要在root用户下运行#操作和普通串口一样,直接收发即可 #发送消息 echo "I'm lubancat" > /dev/ttyS4#接收消息 cat /dev/ttyS4 |
除了这种方法外,还可以使用minicom,具体方法可以参考下列文章 串口章节
使用应用层换流需要在应用层手动控制流控引脚电平实现换流,如果想要发送信息,需要把发送引脚拉高,如果想要接收信息,需要把发送引脚拉低,具体流控引脚根据实际板卡而定。
板卡 | RS485-1流控引脚 | RS485-2流控引脚 |
LubanCat-5IO底板 | GPIO3_PC7 | GPIO3_PD0 |
GPIO的操作方法
引脚编号此处不作过多说明,参考 GPIO控制章节
1、使用sysfs
以LubanCat-5IO底板RS485-1的流控引脚为例,RS485-2的引脚的操作类似
123456789 10 11 12 13 14 15 16 17 | #需要在root用户下运行#使能引脚 echo 119 > /sys/class/gpio/export#设置引脚为输出模式 echo out > /sys/class/gpio/gpio119/direction#设置引脚为低电平 echo 0 > /sys/class/gpio/gpio119/value #流控引脚为低时,数据接收 cat /dev/ttyS4#设置引脚为高电平 echo 1 > /sys/class/gpio/gpio119/value #流控引脚为高时,数据发送 echo "I'm lubancat" > /dev/ttyS4 |
2、使用gpiod
123456789 10 11 | #需要在root用户下运行#设置RS485-1的引脚为高电平 gpioset 3 23=1 #流控引脚为高时,数据发送 echo "I'm lubancat" > /dev/ttyS4#设置RS485-1的引脚为低电平 gpioset 3 23=0 #流控引脚为低时,数据接收 cat /dev/ttyS4 |
8.6.1. 自检测试
该测试需要把两个RS485引脚用线相连接。
RS485-1A | 连接 | RS485-2A |
RS485-1B | 连接 | RS485-2B |
然后需要同时打开RS485-1和RS485-2对应的设备树插件。
8.7. 使用驱动层换流自检测试
根据前面介绍的使能RS485对应的 RS485设备树插件 并且正确将RS485-1和RS485-2相连接后,可进行自检测试。
以LubanCat-5IO底板为例:
1 2 3 4 5 6 7 8 | #需要在root用户下运行#RS485-2后台接收数据 cat /dev/ttyS7 &#RS485-1发送消息 echo "12345678119" > /dev/ttyS4 echo "abcdefghijklmn" > /dev/ttyS4 |
发送完成就可以看到有接收到消息了,如下图所示
提示
驱动层换流只支持中断模式,不支持DMA,以上提示DMA失败使用中断模式是正常的。UART使用DMA传输模式只有在数据量很大时才会产生较为明显的减轻CPU负载的效果。一般情况下,和使用中断传输模式相比,UART使用DMA传输模式并不一定能提高数据传输速度。一方面,CPU性能都很高,传输瓶颈在外设。另一方面,启动DMA需要消耗额外的资源,并且由于UART数据存在长度不确定的特性,会使DMA传输效率下降。
如果想结束后台运行的 cat /dev/ttyS7 &
,参照以下操作
1 | pkill cat |
RS485-1接收&RS485-2发送的方法与上述方法类似,只是将串口编号进行对调,可以自行修改操作。
8.8. 使用应用层换流自检测试
根据前面介绍的使能RS485对应的 串口设备树插件 并且正确将RS485-1和RS485-2相连接后,可进行自检测试。
以LubanCat-5IO底板为例:
123456789 10 11 | #需要在root用户下运行#RS485-2流控脚拉低,数据接收 gpioset 3 24=0 #RS485-2后台接收 cat /dev/ttyS7 &#RS485-1流控脚拉高,数据发送 gpioset 3 23=1 #RS485-1发送消息 echo "12345678119" > /dev/ttyS4 |
发送完成就可以看到有接收到消息了,如下图所示
如果想结束后台运行的 cat /dev/ttyS7 &
,参照以下操作
1 | pkill cat |
RS485-1接收&RS485-2发送的方法与上述方法类似,只是将串口编号进行对调以及修改流控引脚电平,可以自行修改操作
8.8.1. C程序收发示例
8.9. 使用驱动层换流C程序收发示例
使用驱动层换流收发比较简单,和使用普通串口无异。
参考应用开发手册的 串口通讯实验(系统调用) 章节,将串口改为RS485实际对应的串口即可。
8.10. 使用应用层换流C程序收发示例
此处示例在前面自检测试基础上编译C程序进行收发测试,如需实现其他功能可自行参考修改。
此处示例驱动层换流的也可以参考,只需将流控部分代码去掉即可。
根据前面介绍的使能RS485对应的设备树插件并且正确将RS485-1和RS485-2相连接后,可进行后续步骤。
以LubanCat-5IO底板为例:
lubancat_rk_code_storage/quick_start/rs485/rs485_1_test_client.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h>#include <unistd.h> #include <termios.h>#define SERVER_GPIO_INDEX "119" //流控引脚对应编号 const char default_path[] = "/dev/ttyS4"; //rs485-1对应的串口/* *串口配置成功返回0,失败返回-1; */ int set_uart(int fd,int nSpeed, int nBits, char nEvent, int nStop);static int _server_ioctl_init(void) {int fd;//index configfd = open("/sys/class/gpio/export", O_WRONLY);if(fd < 0)return 1;write(fd, SERVER_GPIO_INDEX, strlen(SERVER_GPIO_INDEX));close(fd);//direction configfd = open("/sys/class/gpio/gpio" SERVER_GPIO_INDEX "/direction", O_WRONLY);if(fd < 0)return 2;write(fd, "out", strlen("out"));close(fd);return 0; }static int _server_ioctl_on(void) {int fd;fd = open("/sys/class/gpio/gpio" SERVER_GPIO_INDEX "/value", O_WRONLY);if(fd < 0){printf("_server_ioctl_open error\n");return 1;}write(fd, "1", 1);close(fd);return 0; }static int _server_ioctl_off(void) {int fd;fd = open("/sys/class/gpio/gpio" SERVER_GPIO_INDEX "/value", O_WRONLY);if(fd < 0)return 1;write(fd, "0", 1);close(fd);return 0; }static void _modbus_rtu_server_ioctl(int on) {if (on) {_server_ioctl_on();} else {_server_ioctl_off();} }static int _server_ioctl_exit(void) {int fd;fd = open("/sys/class/gpio/uexport", O_WRONLY);if(fd < 0)return 1;write(fd, SERVER_GPIO_INDEX, strlen(SERVER_GPIO_INDEX));close(fd);}int main(int argc,char *argv[]) {int fd;int res;char *path;//char buf[1024] = "Embedfire 485 send test.\n";char buf[1024] = "1111111\n";char buf1[1024];int i;if(argc > 1){path = argv[1];}else{path = (char *)default_path;}fd = open(path,O_RDWR);if(fd < 0){perror(path);exit(-1);}if( set_uart(fd,115200,8,'n',1) ){printf("set uart error\n");}_server_ioctl_init();for(i=0;i<100;i++){//485-1发送_modbus_rtu_server_ioctl(1);write(fd, buf, strlen(buf));printf("485-1 Send data, res = %d bytes data: %s\r\n",strlen(buf), buf);usleep(10000); // 10毫秒//sleep(1);//485-1读取_modbus_rtu_server_ioctl(0);memset(buf1,0,1024);res = read(fd, buf1, 1024);if(res > 0){printf("485-1 Read data, res = %d bytes data: %s\r\n",res, buf1);}printf("cycle index is %d\r\n",i);usleep(10000); // 10毫秒//sleep(1);}_server_ioctl_exit();close(fd);return 0; }int set_uart(int fd,int nSpeed, int nBits, char nEvent, int nStop) {struct termios opt;//清空串口接收缓冲区tcflush(fd, TCIOFLUSH);//获取串口配置参数tcgetattr(fd, &opt);opt.c_cflag &= (~CBAUD); //清除数据位设置opt.c_cflag &= (~PARENB); //清除校验位设置//opt.c_iflag |= IGNCR; //忽略接收数据中的'\r'字符,在windows中换行为'\r\n'opt.c_iflag &= (~ICRNL); //不将'\r'转换为'\n'opt.c_lflag &= (~ECHO); //不使用回显//设置波特率switch(nSpeed){case 2400:cfsetspeed(&opt,B2400);break;case 4800:cfsetspeed(&opt,B4800);break;case 9600:cfsetspeed(&opt,B9600);break;case 38400:cfsetspeed(&opt,B38400);break;case 115200:cfsetspeed(&opt,B115200);break;default:return -1;}//设置数据位switch(nBits){case 7:opt.c_cflag |= CS7;break;case 8:opt.c_cflag |= CS8;break;default:return -1;}//设置校验位switch(nEvent){//无奇偶校验case 'n':case 'N':opt.c_cflag &= (~PARENB);break;//奇校验case 'o':case 'O':opt.c_cflag |= PARODD;break;//偶校验case 'e':case 'E':opt.c_cflag |= PARENB;opt.c_cflag &= (~PARODD);break;default:return -1;}//设置停止位switch(nStop){case 1:opt.c_cflag &= ~CSTOPB;break;case 2:opt.c_cflag |= CSTOPB;break;default:return -1;}//设置串口tcsetattr(fd,TCSANOW,&opt);return 0; } |
lubancat_rk_code_storage/quick_start/rs485/rs485_2_test_server.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h>#include <unistd.h> #include <termios.h>#define SERVER_GPIO_INDEX "120" //流控引脚的对应编号 const char default_path[] = "/dev/ttyS7"; //rs485-2对应的串口/* *串口配置成功返回0,失败返回-1; */ int set_uart(int fd,int nSpeed, int nBits, char nEvent, int nStop);static int _server_ioctl_init(void) {int fd;//index configfd = open("/sys/class/gpio/export", O_WRONLY);if(fd < 0)return 1;write(fd, SERVER_GPIO_INDEX, strlen(SERVER_GPIO_INDEX));close(fd);//direction configfd = open("/sys/class/gpio/gpio" SERVER_GPIO_INDEX "/direction", O_WRONLY);if(fd < 0)return 2;write(fd, "out", strlen("out"));close(fd);return 0; }static int _server_ioctl_on(void) {int fd;fd = open("/sys/class/gpio/gpio" SERVER_GPIO_INDEX "/value", O_WRONLY);if(fd < 0)return 1;write(fd, "1", 1);close(fd);return 0; }static int _server_ioctl_off(void) {int fd;fd = open("/sys/class/gpio/gpio" SERVER_GPIO_INDEX "/value", O_WRONLY);if(fd < 0)return 1;write(fd, "0", 1);close(fd);return 0; }static void _modbus_rtu_server_ioctl(int on) {if (on) {_server_ioctl_on();} else {_server_ioctl_off();} }static int _server_ioctl_exit(void) {int fd;fd = open("/sys/class/gpio/uexport", O_WRONLY);if(fd < 0)return 1;write(fd, SERVER_GPIO_INDEX, strlen(SERVER_GPIO_INDEX));close(fd);}int main(int argc,char *argv[]) {int fd;int res;char *path;char buf[1024] = "0000000\n";;char buf1[1024];int i;if(argc > 1){path = argv[1];}else{path = (char *)default_path;}fd = open(path,O_RDWR);if(fd < 0){perror(path);exit(-1);}if( set_uart(fd,115200,8,'n',1) ){printf("set uart error\n");}_server_ioctl_init();for(i=0;i<100;i++){//485-2读取_modbus_rtu_server_ioctl(0);memset(buf1,0,1024);res = read(fd, buf1, 1024);if(res > 0){printf("485-2 Read data, res = %d bytes data: %s\r\n",res, buf1);}usleep(10000); // 10毫秒//sleep(1);//485-2发送_modbus_rtu_server_ioctl(1);write(fd, buf, strlen(buf));printf("485-2 Send data, res = %d bytes data: %s\r\n",strlen(buf), buf);printf("cycle index is %d\r\n",i);usleep(10000); // 10毫秒//sleep(1);}_server_ioctl_exit();close(fd);return 0; }int set_uart(int fd,int nSpeed, int nBits, char nEvent, int nStop) {struct termios opt;//清空串口接收缓冲区tcflush(fd, TCIOFLUSH);//获取串口配置参数tcgetattr(fd, &opt);opt.c_cflag &= (~CBAUD); //清除数据位设置opt.c_cflag &= (~PARENB); //清除校验位设置//opt.c_iflag |= IGNCR; //忽略接收数据中的'\r'字符,在windows中换行为'\r\n'opt.c_iflag &= (~ICRNL); //不将'\r'转换为'\n'opt.c_lflag &= (~ECHO); //不使用回显//设置波特率switch(nSpeed){case 2400:cfsetspeed(&opt,B2400);break;case 4800:cfsetspeed(&opt,B4800);break;case 9600:cfsetspeed(&opt,B9600);break;case 38400:cfsetspeed(&opt,B38400);break;case 115200:cfsetspeed(&opt,B115200);break;default:return -1;}//设置数据位switch(nBits){case 7:opt.c_cflag |= CS7;break;case 8:opt.c_cflag |= CS8;break;default:return -1;}//设置校验位switch(nEvent){//无奇偶校验case 'n':case 'N':opt.c_cflag &= (~PARENB);break;//奇校验case 'o':case 'O':opt.c_cflag |= PARODD;break;//偶校验case 'e':case 'E':opt.c_cflag |= PARENB;opt.c_cflag &= (~PARODD);break;default:return -1;}//设置停止位switch(nStop){case 1:opt.c_cflag &= ~CSTOPB;break;case 2:opt.c_cflag |= CSTOPB;break;default:return -1;}//设置串口tcsetattr(fd,TCSANOW,&opt);return 0; } |
1 2 | gcc rs485_1_test_client.c -o rs485_1_test_client gcc rs485_2_test_server.c -o rs485_2_test_server |
打开两个终端,分别运行两测试程序。终端可以是一个串口终端一个ssh终端,或者都使用ssh。
在终端1执行:
1 | sudo ./rs485_2_test_server |
在终端2执行:
1 | sudo ./rs485_1_test_client |
效果如下图所示,485-1和485-2相互收发100次,如果需要发送其他数据,可能需要调整发送和接收数据间的间隔时间,自行测试。
8.10.1. libmodbus简介
libmodbus是一个与使用modbus协议的设备进行数据发送/接收的库,它包含各种后端(backends)通过不同网络进行通信。 (例如,RTU模式下的串口、485总线或TCP / IPv6中的以太网)。 libmodbus还提供了较低通信层的抽象,并在所有支持的平台上提供相同的API。
libmodbus是开源的,它遵循 LGPL v2.1 开源协议,这个协议没有GPL协议那么严格, 简单来说,只要你不修改libmodbus库里面的东西(只调用、链接该库),你是可以闭源你的代码的,也可以用于商业用途,这一点非常好的。
官方代码仓库位于:GitHub - stephane/libmodbus: A Modbus library for Linux, Mac OS, FreeBSD and Windows
提示
如果使用驱动层换流只需对官方代码进行小修改,将串口改为实际串口即可,如果使用应用层换流需要配置rts引脚,野火也提供了示例测试,可进行参考。
8.11. 使用驱动层换流
根据前面介绍的使能RS485对应的 RS485设备树插件 并且正确将RS485-1和RS485-2相连接后,可进行后续步骤。
开发准备,在开发板系统上安装libmodbus-dev和一些编译工具。
1 2 | sudo apt update sudo apt install gcc make git autoconf libtool automake |
1、拉取libmodbus源码并修改
1 | git clone https://github.com/stephane/libmodbus.git |
拉取下来后看到本地有libmodbus文件夹,我们进入 libmodbus/tests目录下,该目录下存放了很多测试程序,此处我们使用unit-test-server.c、unit-test-client.c进行测试。
修改为使用使用的接口,以鲁班猫2金手指/btb底板为例:
打开libmodbus/tests/unit-test-server.c,将代码第57行和第76行的/dev/ttyUSB0修改为实际使用的串口,鲁班猫2金手指/btb底板rs485-1对应/dev/ttyS4
1 2 3 4 5 6 7 8 | #将libmodbus/tests/unit-test-server.c的第57行和第76行定义的接口修改为实际的接口 ... printf("Eg. tcp 127.0.0.1 or rtu /dev/ttyS4\n\n"); ...... ip_or_device = "/dev/ttyS4"; ... |
打开libmodbus/tests/unit-test-client.c,将代码第93行和第112行的/dev/ttyUSB1修改为实际使用的串口,鲁班猫2金手指/btb底板rs485-2对应/dev/ttyS7
1 2 3 4 5 6 7 8 | #将libmodbus/tests/unit-test-client.c的第93行和第112行定义的接口修改为实际的接口 ... printf("Eg. tcp 127.0.0.1 or rtu /dev/ttyS7\n\n"); ...... ip_or_device = "/dev/ttyS7"; ... |
2、编译程序
进入 libmodbus最上层目录 下,运行它提供的脚本,它主要是自动生成一些用于配置的文件。
1 | ./autogen.sh |
打印消息如下
123456789 10 11 | configure.ac:33: installing 'build-aux/compile' configure.ac:56: installing 'build-aux/config.guess' configure.ac:56: installing 'build-aux/config.sub' configure.ac:32: installing 'build-aux/install-sh' configure.ac:32: installing 'build-aux/missing' src/Makefile.am: installing 'build-aux/depcomp' parallel-tests: installing 'build-aux/test-driver'------------------------------------------------------ Initialized build system. You can now run ./configure ------------------------------------------------------ |
运行完毕后,接着运行 configure 去配置编译相关的信息。
1 | ./configure |
最后打印信息如下
123456789 10 11 12 13 14 15 16 | config.status: executing depfiles commands config.status: executing libtool commandslibmodbus 3.1.10===============prefix: /usr/localsysconfdir: ${prefix}/etclibdir: ${exec_prefix}/libincludedir: ${prefix}/includecompiler: gcccflags: -g -O2ldflags:tests: yes |
运行上一步之后,在当前目录下将产生 Makefile 文件,使用 make 命令编译即可:
1 | make |
在编译完成后,在 tests 目录下你会发现有很多可执行的文件,比如我们稍后要运行的程序 unit-test-server、unit-test-client:
123456789 10 11 12 | root@lubancat:~/libmodbus/tests# ls LICENSE bandwidth-server-one unit-test-client.c Makefile bandwidth-server-one.c unit-test-client.o Makefile.am bandwidth-server-one.o unit-test-server Makefile.in random-test-client unit-test-server.c README.md random-test-client.c unit-test-server.o bandwidth-client random-test-client.o unit-test.h bandwidth-client.c random-test-server unit-test.h.in bandwidth-client.o random-test-server.c unit-tests.sh bandwidth-server-many-up random-test-server.o version bandwidth-server-many-up.c stamp-h2 version.c bandwidth-server-many-up.o unit-test-client version.o |
3、运行程序
打开两个终端,一个用于运行服务端一个用于运行客户端。如下所示
在终端1中执行:
1 | ./unit-test-server rtu |
在终端2中执行:
1 | ./unit-test-client rtu |
在 client 终端中最终打印
123456789 10 11 12 13 14 15 16 17 18 19 20 | [11][0F][01][60][00][00][00][02][2B][00][01][00][64][59][63] * try function 0xF: write 0 values: Waiting for a confirmation... <11><8F><03><05><F4> OK [11][0F][01][60][07][B0][06][02][2B][00][01][00][64][12][27] * try function 0xF: write 1968 values: Waiting for a confirmation... <11><8F><03><05><F4> OK [11][42][00][00][00][00][7B][55] Waiting for a confirmation... <11><C2><01><B1><65> Return an exception on unknown function code: OKTEST INVALID INITIALIZATION: The device string is empty OK The baud rate value must not be zero OKALL TESTS PASS WITH SUCCESS. |
以上只是测试使用,关于具体用法请读者参考官方说明自行研究。
8.12. 使用应用层层换流
根据前面介绍的使能RS485对应的 串口设备树插件 并且正确将RS485-1和RS485-2相连接后,可进行后续步骤。
开发准备,在开发板系统上安装libmodbus-dev和一些编译工具。
1 2 | sudo apt update sudo apt install gcc git libmodbus-dev pkg-config |
以鲁班猫2金手指/btb底板为例,其他底板需要自行修改流控引脚编号以及实际的串口接口。
1.服务端程序
lubancat_rk_code_storage/quick_start/rs485/test-server.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | #include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <modbus.h> #include <sys/stat.h> #include <fcntl.h>#define SERVER_GPIO_INDEX "119" //485-1的换流引脚 #define SERVER_ID 17 const uint16_t UT_REGISTERS_TAB[] = { 0x0A, 0x0E, 0x0A, 0x1B,0x0A};static int _server_ioctl_init(void) {int fd;//index configfd = open("/sys/class/gpio/export", O_WRONLY);if(fd < 0)return 1;write(fd, SERVER_GPIO_INDEX, strlen(SERVER_GPIO_INDEX));close(fd);//direction configfd = open("/sys/class/gpio/gpio" SERVER_GPIO_INDEX "/direction", O_WRONLY);if(fd < 0)return 2;write(fd, "out", strlen("out"));close(fd);return 0; }static int _server_ioctl_on(void) {int fd;fd = open("/sys/class/gpio/gpio" SERVER_GPIO_INDEX "/value", O_WRONLY);if(fd < 0)return 1;write(fd, "1", 1);close(fd);return 0; }static int _server_ioctl_off(void) {int fd;fd = open("/sys/class/gpio/gpio" SERVER_GPIO_INDEX "/value", O_WRONLY);if(fd < 0)return 1;write(fd, "0", 1);close(fd);return 0; }static void _modbus_rtu_server_ioctl(modbus_t *ctx, int on) {if (on) {_server_ioctl_on();} else {_server_ioctl_off();} }int main(int argc, char*argv[]) {modbus_t *ctx;modbus_mapping_t *mb_mapping;int rc;int i;uint8_t *query;/*设置串口信息*/ctx = modbus_new_rtu("/dev/ttyS4", 9600, 'N', 8, 1);_server_ioctl_init();/*设置从机地址,设置485模式*/modbus_set_slave(ctx, SERVER_ID);modbus_rtu_set_custom_rts(ctx, _modbus_rtu_server_ioctl);modbus_rtu_set_rts(ctx, MODBUS_RTU_RTS_UP);modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);query = malloc(MODBUS_RTU_MAX_ADU_LENGTH);/*开启调试*/modbus_set_debug(ctx, TRUE);mb_mapping = modbus_mapping_new_start_address(0,0,0,0,0,5,0,0);if (mb_mapping == NULL) {fprintf(stderr, "Failed to allocate the mapping: %s\n",modbus_strerror(errno));modbus_free(ctx);return -1;}/* 初始化值 */for (i=0; i < 5; i++) {mb_mapping->tab_registers[i] = UT_REGISTERS_TAB[i];}rc = modbus_connect(ctx);if (rc == -1) {fprintf(stderr, "Unable to connect %s\n", modbus_strerror(errno));modbus_free(ctx);return -1;}modbus_flush(ctx);for (;;) {do {rc = modbus_receive(ctx, query);} while (rc == 0);rc = modbus_reply(ctx, query, rc, mb_mapping);if (rc == -1) {break;}}modbus_mapping_free(mb_mapping);free(query);modbus_close(ctx);modbus_free(ctx);return 0; } |
2.客户端程序
lubancat_rk_code_storage/quick_start/rs485/test-client.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | #include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <modbus.h> #include <sys/stat.h> #include <fcntl.h>#define CLIENT_GPIO_INDEX "91" //485-2的换流引脚 #define SERVER_ID 17static int _client_ioctl_init(void) {int fd;//index configfd = open("/sys/class/gpio/export", O_WRONLY);if(fd < 0)return 1 ;write(fd, CLIENT_GPIO_INDEX, strlen(CLIENT_GPIO_INDEX));close(fd);//direction configfd = open("/sys/class/gpio/gpio" CLIENT_GPIO_INDEX "/direction", O_WRONLY);if(fd < 0)return 2;write(fd, "out", strlen("out"));close(fd);return 0; }static int _client_ioctl_on(void) {int fd;fd = open("/sys/class/gpio/gpio" CLIENT_GPIO_INDEX "/value", O_WRONLY);if(fd < 0)return 1;write(fd, "0", 1);close(fd);return 0; }static int _client_ioctl_off(void) {int fd;fd = open("/sys/class/gpio/gpio" CLIENT_GPIO_INDEX "/value", O_WRONLY);if(fd < 0)return 1;write(fd, "1", 1);close(fd);return 0; }static void _modbus_rtu_client_ioctl(modbus_t *ctx, int on) {if (on) {_client_ioctl_on();} else {_client_ioctl_off();} }int main(int argc, char *argv[]) {modbus_t *ctx = NULL;int i,rc;uint16_t tab_rp_registers[5] = {0}; //定义存放数据的数组/*创建一个RTU类型的变量*//*设置要打开的串口设备 波特率 奇偶校验 数据位 停止位*/ctx = modbus_new_rtu("/dev/ttyS7", 9600, 'N', 8, 1);if (ctx == NULL) {fprintf(stderr, "Unable to allocate libmodbus context\n");return -1;}/*设置485模式*/_client_ioctl_init();modbus_rtu_set_custom_rts(ctx, _modbus_rtu_client_ioctl);modbus_rtu_set_rts(ctx, MODBUS_RTU_RTS_DOWN);modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);/*设置debug模式*/modbus_set_debug(ctx, TRUE);/*设置从机地址*/modbus_set_slave(ctx, SERVER_ID);/*RTU模式 打开串口*/if (modbus_connect(ctx) == -1) {fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));modbus_free(ctx);return -1;}//读取多个连续寄存器rc = modbus_read_registers(ctx, 0, 5, tab_rp_registers);if (rc == -1){fprintf(stderr,"%s\n", modbus_strerror(errno));return -1;}for (i=0; i<5; i++){//打印读取的数据printf("reg[%d] = %d(0x%x)\n", i, tab_rp_registers[i], tab_rp_registers[i]);}modbus_close(ctx);modbus_free(ctx);return 0; } |
将两个程序的源文件传输到开发板系统,然后使用下面命令编译:
1 2 | gcc test-server.c -o test-server `pkg-config --cflags --libs libmodbus` gcc test-client.c -o test-client `pkg-config --cflags --libs libmodbus` |
打开两个终端,一个用于运行服务端一个用于运行客户端(ssh终端和串口终端)。如下所示:
服务终端
1 | root@lubancat:~$./test-server |
客户终端
1 | root@lubancat:~$./test-client |
在客户终端中最终打印
客户终端
123456789 10 | Opening /dev/ttyS7 at 9600 bauds (N, 8, 1) [0A][03][00][00][00][05][84][B2] Sending request using RTS signal Waiting for a confirmation... <0A><03><0A><00><0A><00><0E><00><0A><00><1B><00><0A><C7><5C> reg[0] = 10(0xa) reg[1] = 14(0xe) reg[2] = 10(0xa) reg[3] = 27(0x1b) reg[4] = 10(0xa) |
在服务终端中最终打印
服务终端
1 2 3 4 5 6 7 | Opening /dev/ttyS4 at 9600 bauds (N, 8, 1)Bytes flushed (0)Waiting for a indication...<0A><03><00><00><00><05><84><B2>[0A][03][0A][00][0A][00][0E][00][0A][00][1B][00][0A][C7][5C]Sending request using RTS signalWaiting for a indication... |
以上代码均做测试用,关于具体用法请读者自行研究。 参考资料: GitHub - stephane/libmodbus: A Modbus library for Linux, Mac OS, FreeBSD and Windows
相关文章:
RS485和RS232 通信配置
RS232 目前硬件上支持RS232的有以下板卡: LubanCat-5IO底板(含有RS232x2) 7.1. 引脚定义 具体的引脚定义可以参考背面的丝印 LubanCat-5IO底板 引脚定义图 7.2. 跳帽配置 LubanCat-5IO底板 鲁班买5IO底板上的RS485和RS232是共用同一组…...
【高数上册笔记篇02】:数列与函数极限
【参考资料】 同济大学《高等数学》教材樊顺厚老师B站《高等数学精讲》系列课程 (注:本笔记为个人数学复习资料,旨在通过系统化整理替代厚重教材,便于随时查阅与巩固知识要点) 仅用于个人数学复习,因为课…...
【网络安全】——大端序(Big-Endian)和小端序(Little-Endian)
字节序(Endianness)是计算机系统中多字节数据(如整数、浮点数)在内存中存储或传输时,字节排列顺序的规则。它分为两种类型:大端序(Big-Endian)和小端序…...
机器学习极简入门:从基础概念到行业应用
有监督学习(supervised learning) 让模型学习的数据包含正确答案(标签)的方法,最终模型可以对无标签的数据进行正确处理和预测,可以分为分类与回归两大类 分类问题主要是为了“尽可能分开整个数据而画线”…...
MIT XV6 - 1.5 Lab: Xv6 and Unix utilities - xargs
接上文 MIT XV6 - 1.4 Lab: Xv6 and Unix utilities - find xargs 继续实验,实验介绍和要求如下 (原文链接 译文链接) : Write a simple version of the UNIX xargs program for xv6: its arguments describe a command to run, it reads lines from the standard …...
Springboot整合Swagger3
Springboot整合Swagger3、常用注解解释、访问Swagger地址出现404、403、拒绝访问等问题_swagger3注解-CSDN博客...
经典音乐播放器——完美歌词 Poweramp Music Player 3 build
—————【下 载 地 址】——————— 【本章单下载】:https://drive.uc.cn/s/d6c480bc47604 【百款黑科技】:https://ucnygalh6wle.feishu.cn/wiki/HPQywvPc7iLZu1k0ODFcWMt2n0d?fromfrom_copylink —————【下 载 地 址】——————— 本…...
锚定基础与拥抱融合:C 语言在编程教育与技术社区的破圈之路
引言 在 Python 占据 TIOBE 指数榜首的 2025 年,C 语言以 23.4% 的稳定份额(2025 年 5 月数据)持续稳居前三,这一现象在编程教育领域尤为显著:全球 92% 的计算机科学本科课程仍将 C 语言作为必修基础课,而…...
深度学习入门:从神经网络基础到前向传播全面解析
深度学习入门:从神经网络基础到前向传播全面解析 🔥 重磅干货! 本文是《深度学习基础与核心技术详解》专栏的开篇之作,将系统性地带你走进深度学习的世界!建议收藏+关注,错过可能要找很久哦~ 目录 深度学习概述神经网络基础 2.1 生物神经元与人工神经元2.2 感知机模型2.…...
Lambda表达式能用在哪些场景?
Lambda表达式是Java 8引入的一种强大特性,它允许以简洁的方式表示匿名函数(即没有名字的函数)。Lambda表达式可以用于许多场景,尤其是在与函数式接口、Stream API、并发编程等结合时,能够显著简化代码并提高开发效率。…...
英语听力口语词汇--2.宣传类
1.approach uk /əˈprəʊtʃ/ n.(思考问题的)方式,方法,态度 2.foreign uk /ˈfɒr.ən/ adj.外国的 3.alliance uk /əˈlaɪ.əns/ n.结盟国家(或团体),同盟国家(或团体)&...
『 测试 』测试基础
文章目录 1. 调试与测试的区别2. 开发过程中的需求3. 开发模型3.1 软件的生命周期3.2 瀑布模型3.2.1 瀑布模型的特点/缺点 3.3 螺旋模型3.3.1 螺旋模型的特点/缺点 3.4 增量模型与迭代模型3.5 敏捷模型3.5.1 Scrum模型3.5.2 敏捷模型中的测试 4 测试模型4.1 V模型4.2 W模型(双V…...
Pandas 时间处理利器:to_datetime() 与 Timestamp() 深度解析
Pandas 时间处理利器:to_datetime() 与 Timestamp() 深度解析 在数据分析和处理中,时间序列数据扮演着至关重要的角色。Pandas 库凭借其强大的时间序列处理能力,成为 Python 数据分析领域的佼佼者。其中,to_datetime() 函数和 Ti…...
支持向量机的回归用法详解
支持向量机的回归用法详解 在机器学习的广阔领域中,支持向量机(SVM)是一种极具影响力的算法,它不仅在分类任务上表现出色,在回归任务中同样有着独特的应用价值。本文将深入探讨 SVM 的回归用法,包括其基本…...
计算机基础
今天不和大家分享算法了,最近为什么一直分享算法题,一个是因为最近很忙加上状态不太在线,第二个是因为我报了ICPC的比赛,也就是大学生程序设计大赛,所以平时刷算法比较多一些,虽然说结果上也没有很多的收获…...
用C语言实现的——一个支持完整增删查改功能的二叉排序树BST管理系统,通过控制台实现用户与数据结构的交互操作。
一、知识回顾 二叉排序树(Binary Search Tree,BST),又称二叉查找树或二叉搜索树,是一种特殊的二叉树数据结构。 基本性质: ①有序性 对于树中的每个节点,其左子树中所有节点的值都小于该节点的…...
uniapp-商城-53-后台 商家信息(更新修改和深浅copy)
1、概述 文章主要讨论了在数据库管理中如何处理用户上传和修改商家信息的问题,特别是通过深浅拷贝技术来确保数据更新的准确性和安全性。 首先,解释了深拷贝和浅拷贝的区别:浅拷贝使得两个变量共享相同的内存地址,而深拷贝则创建新…...
vue数据可视化开发echarts等组件、插件的使用及建议-浅看一下就行
在 Vue 项目中使用 ECharts 进行数据可视化开发时,可以结合 Vue 的响应式特性和 ECharts 的强大功能,实现动态、交互式的图表展示。 一、ECharts 基础使用 1. 安装 ECharts npm install echarts2. 在 Vue 组件中使用 ECharts <template><div…...
百度AI战略解析:文心一言与自动驾驶的双轮驱动
百度AI战略解析:文心一言与自动驾驶的双轮驱动 系统化学习人工智能网站(收藏):https://www.captainbed.cn/flu 文章目录 百度AI战略解析:文心一言与自动驾驶的双轮驱动摘要引言一、技术架构:大模型与自动…...
MCP Streamable HTTP 传输层的深度解析及实战分析
一、Streamable HTTP 传输层设计革新 1. 核心设计思想 协议融合:将 HTTP/1.1、HTTP/2 与 SSE 协议特性深度整合动态协商:通过 HTTP Header 实现传输协议动态协商(X-MCP-Transport)流式优先:默认启用流式传输,支持半双工通信背压控制:基于 HTTP/2 流级流量控制实现智能速…...
六大设计模式--OCP(开闭原则):构建可扩展软件的基石
写在前面:一个真实的项目悲剧 某电商平台促销功能每次迭代都需要修改核心订单类,导致: ✅ 双十一活动修改导致支付功能崩溃 ✅ 新人优惠引发会员系统连环故障 ✅ 每次发布需全量回归测试 根本原因:系统架构违反开闭原则 一、开闭…...
ActiveMQ 生产环境问题排查与调优指南(一)
一、引言 在当今复杂的分布式系统架构中,消息中间件扮演着至关重要的角色,而 ActiveMQ 作为一款广泛使用的开源消息中间件,凭借其丰富的特性、良好的稳定性和易用性,在众多企业的生产环境中占据了一席之地。它基于 JMS(…...
深入理解 JavaScript 中的 FileReader API:从理论到实践
文章目录 深入理解 JavaScript 中的 FileReader API:从理论到实践前言什么是 FileReader?核心特性 FileReader 的常用方法事件监听实际案例案例 1:读取文本文件内容案例 2:图片预览(Data URL)案例 3&#x…...
Google LLM prompt engineering(谷歌提示词工程指南)
文章目录 基本概念AI输出配置:调整AI的回答方式输出长度温度(Temperature)Top-K和Top-P 提示技术:让AI更好地理解你零样本提示(Zero-shot)少样本提示(Few-shot)系统提示(…...
前端npm包发布流程:从准备到上线的完整指南
无论是使用第三方库还是创建和分享自己的工具,npm都为我们提供了一个强大而便捷的平台,然而很多开发者在将自己的代码发布到npm上时往往面临各种困惑和挑战,本篇文章将从准备工作到发布上线,探讨如何让npm包更易发布及避免常见的坑…...
【MySQL】表空间结构 - 从何为表空间到段页详解
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
OB Cloud 云数据库V4.3:SQL +AI全新体验
OB Cloud 云数据库V4.3:SQL AI全新体验 简介 OB Cloud云数据库全新升级至V4.3版本,为用户带来了SQLAI的最新技术体验,强化数据库的传统功能,深度融合了人工智能技术,引入先进的向量检索功能和优化的SQL引擎,…...
【Linux系统】第四节—详解yum+vim
hello 我是云边有个稻草人 Linux—本节课所属专栏—欢迎订阅—持续更新中~ 目录 画板—本节课知识点详解 一、软件包管理器 1.1 什么是软件包 1.2 Linux软件⽣态 1.3 yum具体操作 【查看软件包】 【安装软件】 【卸载软件】 【注意事项】 1.4 安装源 二、vim 2.1 …...
Git的核心作用详解
一、版本控制与历史追溯 Git作为分布式版本控制系统,其核心作用是记录代码的每一次修改,形成完整的历史记录。通过快照机制,Git会保存每次提交时所有文件的完整状态(而非仅记录差异),确保开发者可以随时回…...
Three.js + React 实战系列 - 职业经历区实现解析 Experience 组件✨(互动动作 + 3D 角色 + 点击切换动画)
对个人主页设计和实现感兴趣的朋友可以订阅我的专栏哦!!谢谢大家!!! 在这篇博客中,我们将分析一个极其有趣和互动性的组件 - Experience.jsx,该组件用于在主页中呈现个人的工作经历。 这个组件…...
3D虚拟工厂vue3+three.js
1、在线体验 3D虚拟工厂在线体验 2、功能介绍 1. 全屏显示功能2. 镜头重置功能3. 企业概况信息模块4. 标签隐藏/显示功能5. 模型自动旋转功能6. 办公楼分层分解展示7. 白天/夜晚 切换8. 场景资源预加载功能9. 晴天/雨天/雾天10. 无人机视角模式11. 行人漫游视角模式12. 键盘…...
[Java实战]Spring Boot 解决跨域问题(十四)
[Java实战]Spring Boot 解决跨域问题(十四) 一、CORS 问题背景 什么是跨域问题? 当浏览器通过 JavaScript 发起跨域请求(不同协议、域名、端口)时,会触发同源策略限制,导致请求被拦截。 示例场…...
嵌入式硬件篇---CAN
文章目录 前言1. CAN协议基础1.1 物理层特性差分信号线终端电阻通信速率总线拓扑 1.2 帧类型1.3 数据帧格式 2. STM32F103RCT6的CAN硬件配置2.1 硬件连接2.2 CubeMX配置启用CAN1模式波特率引脚分配过滤器配置(可选) 3. HAL库代码实现3.1 CAN初始化3.2 发…...
(2025)图文解锁RAG从原理到代码实操,代码保证可运行
什么是RAG RAG(检索增强生成)是一种将语言模型与可搜索知识库结合的方法,主要包含以下关键步骤: 数据预处理 加载:从不同格式(PDF、Markdown等)中提取文本分块:将长文本分割成短序列(通常100-500个标记),作为检索单元…...
TWAS、GWAS、FUSION
全基因组关联研究(GWAS,Genome-Wide Association Study)是一种统计学方法,用于在全基因组水平上识别与特定性状或疾病相关的遗传变异。虽然GWAS可以识别与性状相关的遗传信号,但它并不直接揭示这些遗传变异如何影响生物…...
大模型微调终极方案:LoRA、QLoRA原理详解与LLaMA-Factory、Xtuner实战对比
文章目录 一、微调概述1.1 微调步骤1.2 微调场景 二、微调方法2.1 三种方法2.2 方法对比2.3 关键结论 三、微调技术3.1 微调依据3.2 LoRA3.2.1 原理3.2.2 示例 3.3 QLoRA3.4 适用场景 四、微调框架4.1 LLaMA-Factory4.2 Xtuner4.3 对比 一、微调概述 微调(Fine-tun…...
FHE 之 面向小白的引导(Bootstrapping)
1. 引言 FHE初学者和工程师常会讨论的一个问题是; “什么是引导(bootstrapping)?” 从理论角度看,这个问题的答案很简单: 引导就是套用 Gentry 提出的思想——在加密状态下同态地执行解密操作ÿ…...
安装:Kali2025+Docker
安装:Kali2025Docker Kali2025安装 直接官网下载WMware版本 https://www.kali.org/get-kali/#kali-virtual-machines 直接打开运行 初始用户密码 kali/kali sudo -i 命令切换到root 更换镜像 切换到其他可用的 Kali Linux 镜像源可能会解决问题,可以使用国内的镜像源&…...
什么是深拷贝什么是浅拷贝,两者区别
什么是深拷贝什么是浅拷贝,两者区别 1.深拷贝 递归复制对象的所有层级,嵌套的引用类型属性,最后生成一个完全独立的新对象,与原对象无任何引用关联。 特点: 新对象和原对象的所有层级属性是独立的(修改…...
A2A大模型协议及Java示例
A2A大模型协议概述 1. 协议作用 A2A协议旨在解决以下问题: 数据交换:不同应用程序之间的数据格式可能不一致,A2A协议通过定义统一的接口和数据格式解决这一问题。模型调用:提供标准化的接口,使得外部应用可以轻松调…...
第七章 数据库编程
1 数据库编程基础 1.1 数据库系统概述 数据库系统是由数据库、数据库管理系统(DBMS)和应用程序组成的完整系统。其主要目的是高效地存储、管理和检索数据。现代数据库系统通常分为以下几类: 关系型数据库(RDBMS):如MySQL、PostgreSQL、Oracle等&#x…...
电影感户外哑光人像自拍摄影Lr调色预设,手机滤镜PS+Lightroom预设下载!
调色详情 电影感户外哑光人像自拍摄影 Lr 调色,是借助 Lightroom 软件,针对户外环境下拍摄的人像自拍进行后期处理。旨在模拟电影画面的氛围与质感,通过调色赋予照片独特的艺术气息。强调打造哑光效果,使画面色彩不过于浓烈刺眼&a…...
C++--类的构造函数与初始化列表差异
一,引言 在类中成员函数的构造函数担任其将对象初始化的作用,而初始化列表也有着相似的作用。大部分人建议都是初始化列表进行初始化,本文主要进行讲解二者的区别。 首先看一下构造函数的初始化方式: #define _CRT_SECURE_NO…...
深入浅出之STL源码分析4_类模版
1.引言 我在上面的文章中讲解了vector的基本操作,然后提出了几个问题。 STL之vector基本操作-CSDN博客 1.刚才我提到了我的编译器版本是g 11.4.0,而我们要讲解的是STL(标准模板库),那么二者之间的关系是什么&#x…...
Lambda表达式解读
本文通过具体案例演示函数式接口Function<T,R>的三种实现方式演变过程。 一、传统匿名内部类实现 Integer resInt1 t1(new Function<String, Integer>() {Overridepublic Integer apply(String s) {int i Integer.parseInt(s);return i;} });实现特点࿱…...
PySide6 GUI 学习笔记——常用类及控件使用方法(常用类边距QMarginsF)
文章目录 类简介方法总览关键说明示例代码 类简介 QMarginsF 用于定义四个浮点型边距(左、上、右、下),描述围绕矩形的边框尺寸。所有边距接近零时 isNull() 返回 True,支持运算符重载和数学运算。 方法总览 方法名/运算符参数返…...
Android方法耗时监控插件开发
需求:自定义一个Gradle插件,这个Gradle插件可以统计方法的耗时,并当方法耗时超过阈值时,可以通过打印Log日志在控制台,然后可以通过Log定位到耗时方法的位置,帮助我们找出耗时方法和当前线程名,…...
TWAS / FUSION
FUSION 是一套用于执行转录组范围和调控组范围关联研究(TWAS 和 RWAS)的工具。它通过构建功能/分子表型的遗传成分的预测模型,并使用 GWAS 汇总统计数据预测和测试该成分与疾病的关联,目标是识别 GWAS 表型与仅在参考数据中测量的…...
C++中的static_cast:类型转换的安全卫士
C中的static_cast:类型转换的安全卫士 在C编程中,类型转换是不可避免的操作,而static_cast作为C四大强制类型转换运算符之一,是最常用且相对安全的一种转换方式。今天我们就来深入探讨一下这个重要的类型转换工具。 一、static_…...
uniapp-商城-51-后台 商家信息(logo处理)
前面对页面基本进行了梳理和说明,特别是对验证规则进行了阐述,并对自定义规则的兼容性进行了特别补充,应该说是干货满满。不知道有没有小伙伴已经消化了。 下面我们继续前进,说说页面上的logo上传组件,主要就是uni-fil…...