且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

am335x Qt SocketCAN Demo hacking

更新时间:2022-08-12 20:28:37

/***********************************************************************************
 *                    am335x Qt SocketCAN Demo hacking
 *  说明:
 *      本源代码来自OK335xS,主要是为分析如何在Qt中使用SocketCAN的这种通信方式。
 *
 *                                          2015-9-12 晴 深圳 南山平山村 曾剑锋
 **********************************************************************************/


cat main.c #include <QtGui/QApplication> #include "mainwindow.h" #include "myinputpanelcontext.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); /** * 创建软键盘 */ MyInputPanelContext *ic = new MyInputPanelContext; /** * This function replaces the QInputContext instance used by the application with inputContext. * Qt takes ownership of the given inputContext. * * 设置软键盘 */ a.setInputContext(ic); //将输入上下文与应用程序关联 MainWindow w; /** * Qt::WindowMaximizeButtonHint: * Adds a maximize button. On some platforms this implies Qt::WindowSystemMenuHint for it to work. * Qt::WindowMinimizeButtonHint: * Adds a minimize button. On some platforms this implies Qt::WindowSystemMenuHint for it to work. */ w.setWindowFlags(w.windowFlags()& ~Qt::WindowMaximizeButtonHint& ~Qt::WindowMinimizeButtonHint); w.showMaximized(); w.show(); return a.exec(); } cat mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QProcess> #include <sys/ioctl.h> #include <net/if.h> #include <linux/can.h> #include "thread.h" #include <QButtonGroup> /** * 引入界面文件生成的UI类 */ namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); protected: void moveEvent(QMoveEvent *); // 界面移动事件 void resizeEvent(QResizeEvent *); // 界面重绘大小事件 void closeEvent(QCloseEvent *); // 界面关闭事件 private slots: void on_send_clicked(); // 发送按钮点击槽 void msg(QString str); // 信息处理信号槽 void stopcan(int v); // 停止can信号槽 void startcan(int v); // 开始can信号槽 void on_can0_toggled(bool checked); // can0 被点击翻转信号槽 void on_can1_toggled(bool checked); // can1 被点击翻转信号槽 private: Ui::MainWindow *ui; // 声明图形界面指针 int socket; // can socket 描述符 struct sockaddr_can addr; // can socket 地址结构体 Thread *t; // 线程 QButtonGroup* btg; // 按钮 }; #endif // MAINWINDOW_H cat mainwindow.c #include <sys/ioctl.h> #include <fcntl.h> #include "mainwindow.h" #include "ui_mainwindow.h" #include <QMessageBox> #include <unistd.h> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), // 构造函数初始化父类构造函数 ui(new Ui::MainWindow) // 创建ui实体,并赋值 { ui->setupUi(this); // 设置将图形界面放置在哪个对象上 btg = new QButtonGroup; // 创建分组,多选一 btg->addButton(ui->can0,0); btg->addButton(ui->can1,1); startcan(0); } MainWindow::~MainWindow() { /** * 这里让人怀疑,都已经删除了ui界面,button group又怎么能够获取到ui里面的checkedId呢? * 唯一的解释就是button group里面保存了最后一次toggle事件获取的checkedId值,而不是运行 * checkedId()的时候去获取checkedId */ delete ui; stopcan(btg->checkedId()); } void MainWindow::msg(QString str) { /** * 将获取到的信息放到显示文本框的最后去 */ ui->label->append(str); } void MainWindow::on_send_clicked() { /** * struct can_frame - basic CAN frame structure * @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition * @can_dlc: frame payload length in byte (0 .. 8) aka data length code * N.B. the DLC field from ISO 11898-1 Chapter 8.4.2.3 has a 1:1 * mapping of the 'data length code' to the real payload length * @data: CAN frame payload (up to 8 byte) * * struct can_frame { * canid_t can_id; // 32 bit CAN_ID + EFF/RTR/ERR flags * __u8 can_dlc; // frame payload length in byte (0 .. CAN_MAX_DLEN) * __u8 data[CAN_MAX_DLEN] __attribute__((aligned(8))); * }; */ struct can_frame frame; /** * 获取将要发送的文本内容 * Returns a std::string object with the data contained in this QString. * The Unicode data is converted into 8-bit characters using the toUtf8() function. */ std::string str=ui->edit->text().toStdString(); /** * 如果文本的长度大于8,那么将给出提示信息,并直接返回,不发送数据 */ if(str.length() > 8) { QMessageBox::about(this,"error","length of send string must less than 8 bytes"); return; } /** * 默认发送的can id是0x123 * * 扩展格式识别符由 29 位组成。其格式包含两个部分:11 位基本 ID、18 位扩展 ID。 * Controller Area Network Identifier structure: * * bit 0-28 : CAN识别符 (11/29 bit) * bit 29 : 错误帧标志 (0 = data frame, 1 = error frame) * bit 30 : 远程发送请求标志 (1 = rtr frame) * bit 31 : 帧格式标志 (0 = standard 11 bit, 1 = extended 29 bit) * * typedef __u32 canid_t; * * struct can_frame { * canid_t can_id; // 32 bit CAN_ID + EFF/RTR/ERR flags * __u8 can_dlc; // 数据长度: 0 .. 8 * __u8 data[8] __attribute__((aligned(8))); * }; */ frame.can_id = 0x123; /** * Returns a pointer to an array that contains a null-terminated sequence of * characters (i.e., a C-string) representing the current value of the string object. * This array includes the same sequence of characters that make up the value of * the string object plus an additional terminating null-character ('\0') at the end. */ strcpy((char*)frame.data, str.c_str()); /** * 目前猜测:dlc --> data length count * 于是可以很好的解释:can 发送的数据长度,是按字节算的 */ frame.can_dlc = str.length(); /** * Send N bytes of BUF on socket FD to peer at address ADDR (which is * ADDR_LEN bytes long). Returns the number sent, or -1 for errors. * * This function is a cancellation point and therefore not marked with * __THROW. * * extern ssize_t sendto (int __fd, __const void *__buf, size_t __n, * int __flags, __CONST_SOCKADDR_ARG __addr, * socklen_t __addr_len); */ sendto(socket,&frame,sizeof(struct can_frame),0,(struct sockaddr*)&addr,sizeof(addr)); } void MainWindow::moveEvent(QMoveEvent *) { /** * 让测试窗口不会被移动 */ this->move(QPoint(0,0)); } void MainWindow::resizeEvent(QResizeEvent *) { /** * 让窗口最大化显示 */ this->showMaximized(); } void MainWindow::closeEvent(QCloseEvent *) { /** * 直接退出,不用关闭can口么?也就是说程序会在真正推出前,先解析掉所有当前生成的类实体? */ exit(0); } void MainWindow::startcan(int v) { /** * 从提示信息可以看出,使用125000作为默认的波特率 */ if(v == 0) { system("canconfig can0 bitrate 125000 ctrlmode triple-sampling on"); system("canconfig can0 start"); } else { system("canconfig can1 bitrate 125000 ctrlmode triple-sampling on"); system("canconfig can1 start"); } /** * Create a new socket of type TYPE in domain DOMAIN, using * protocol PROTOCOL. If PROTOCOL is zero, one is chosen automatically. * Returns a file descriptor for the new socket, or -1 for errors. * * extern int socket (int __domain, int __type, int __protocol) __THROW; * * #define PF_CAN 29 // Controller Area Network. * * SOCK_RAW = 3, // Raw protocol interface. * #define SOCK_RAW SOCK_RAW * * particular protocols of the protocol family PF_CAN * #define CAN_RAW 1 // RAW sockets * */ socket = ::socket(PF_CAN, SOCK_RAW, CAN_RAW); /** * struct ifreq * { * # define IFHWADDRLEN 6 * # define IFNAMSIZ IF_NAMESIZE * union * { * char ifrn_name[IFNAMSIZ]; // Interface name, e.g. "en0". * } ifr_ifrn; * * union * { * struct sockaddr ifru_addr; * struct sockaddr ifru_dstaddr; * struct sockaddr ifru_broadaddr; * struct sockaddr ifru_netmask; * struct sockaddr ifru_hwaddr; * short int ifru_flags; * int ifru_ivalue; * int ifru_mtu; * struct ifmap ifru_map; * char ifru_slave[IFNAMSIZ]; // Just fits the size * char ifru_newname[IFNAMSIZ]; * __caddr_t ifru_data; * } ifr_ifru; * }; * # define ifr_name ifr_ifrn.ifrn_name // interface name * # define ifr_hwaddr ifr_ifru.ifru_hwaddr // MAC address * # define ifr_addr ifr_ifru.ifru_addr // address * # define ifr_dstaddr ifr_ifru.ifru_dstaddr // other end of p-p lnk * # define ifr_broadaddr ifr_ifru.ifru_broadaddr // broadcast address * # define ifr_netmask ifr_ifru.ifru_netmask // interface net mask * # define ifr_flags ifr_ifru.ifru_flags // flags * # define ifr_metric ifr_ifru.ifru_ivalue // metric * # define ifr_mtu ifr_ifru.ifru_mtu // mtu * # define ifr_map ifr_ifru.ifru_map // device map * # define ifr_slave ifr_ifru.ifru_slave // slave device * # define ifr_data ifr_ifru.ifru_data // for use by interface * # define ifr_ifindex ifr_ifru.ifru_ivalue // interface index * # define ifr_bandwidth ifr_ifru.ifru_ivalue // link bandwidth * # define ifr_qlen ifr_ifru.ifru_ivalue // queue length * # define ifr_newname ifr_ifru.ifru_newname // New name * # define _IOT_ifreq _IOT(_IOTS(char),IFNAMSIZ,_IOTS(char),16,0,0) * # define _IOT_ifreq_short _IOT(_IOTS(char),IFNAMSIZ,_IOTS(short),1,0,0) * # define _IOT_ifreq_int _IOT(_IOTS(char),IFNAMSIZ,_IOTS(int),1,0,0) * * SIOCGIFINDEX: * Retrieve the interface index of the interface into ifr_ifindex. */ struct ifreq ifr; strcpy((char *)(ifr.ifr_name),v == 0 ? "can0" : "can1"); ioctl(socket,SIOCGIFINDEX,&ifr); /** * 绑定socket */ addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; bind(socket,(struct sockaddr*)&addr,sizeof(addr)); /** * 这里是存在问题,每次线程并没有关闭,而是直接不管,可能导致一些问题 * 从后续的代码可以看出,每次都已经停止了线程,delete了对象实体。 */ t = NULL; /** * 创建线程,并将socket文件描述符作为参数传入线程,主要作为接受数据线程 */ t = new Thread(socket); /** * 用于处理线程发出的信号 */ connect(t,SIGNAL(msg(QString)),this,SLOT(msg(QString))); /** * 开启线程 */ t->start(); } void MainWindow::stopcan(int v) { /** * 关闭对应的can之前,先关闭对应socket接受线程 */ if(t) { t->stop(); t->deleteLater(); } /** * 关闭socket文件描述符 */ ::close(socket); /** * 给出相关的提示信息 */ if(v == 0) system("canconfig can0 stop"); else system("canconfig can1 stop"); } void MainWindow::on_can0_toggled(bool checked) { /** * 根据对应的情况,打开,或者关闭对应的can设备 */ if(checked) { stopcan(1); startcan(0); } } void MainWindow::on_can1_toggled(bool checked) { /** * 根据对应的情况,打开,或者关闭对应的can设备 */ if(checked) { stopcan(0); startcan(1); } }