Linux 驱动像单片机一样读取一帧dmx512串口数据

硬件全志R528

目标:实现Linux 读取一帧dmx512串口数据。

问题分析:因为串口数据量太大,帧与帧之间的间隔太小。通过Linux自带的读取函数方法无法获取到

帧头和帧尾,读取到的数据都是缓存区中的,数据量又大。导致缓冲区中一直有很多数据,

又由于dmx512数据协议中并没有帧头帧尾字段只有普通数据,无法通过特定的帧头帧尾截取到一完整帧的数据。

所以只能像单片机一样通过串口寄存器对LSR 的UART_LSR_FE位 (接收到错误帧)认为是一帧结束和开始。

通过对Linux驱动读取串口数据的过程分析,

tty_read() ----> ld->ops->read() ----> n_tty_read()
n_tty_read()中add_wait_queue(&tty->read_wait, &wait)没有数据的时候上层的read进程阻塞在此
而在串口有数据来的时候n_tty_receive_buf()--->wake_up_interruptible(&tty->read_wait),唤醒上面的read进程n_tty_read()中会继续运行,将数据拷到用户空间
从整个分析来看,uart驱动会把从硬件接受到的数据暂时存放在tty_buffer里面,然后调用线路规程的receive_buf()把数据存放到tty->read_buf里面,

而系统调用的read()函数直接从tty->read_buf里面读取数据。

所以最终判断在uart的串口中断接收处理函数中增加接收代码比较合适。

 

 Linux 设置非标准波特率参考上次的博客。

方法:

1、写一个简单字符驱动dmx512_uart.c,放在sunxi-uart.c同文件夹中。

在驱动读函数中设置全局变量标识,等待读取数据,后copy_to_user上传到用户空间.

修改同目录下的Makefile 和Kconfig 后添加到内核,编译到内核中。

 

/*dmx512_uart.c 代码*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include "dmx512_uart.h"

#define CDEV_NAME  "dmx512_uart_dev"
struct dmx512_uart_dev *dmx512_devp;


static ssize_t dmx512drv_read (struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
    int len =0;
    int num =0;
    int ret =0;
    int i=0;
    //printk("%s start\n",__func__);

    if(size > DMX512_BUF_LEN)
    {
        dmx512_devp->r_size = DMX512_BUF_LEN;
    }
    else
    {
        dmx512_devp->r_size = size;
    }
    memset(dmx512_devp->dmx_buff,0,sizeof(dmx512_devp->dmx_buff));
    dmx512_devp->end_read_flag = false;
    dmx512_devp->recv_len =0;
    dmx512_devp->num_break =0;
    dmx512_devp->start_read_flag = true;

    while(!dmx512_devp->end_read_flag) /*等待获取数据*/
    {
        msleep(100);
        num++;
        if(num > 50)
        {
            printk("timeout\n");
            break;
        }
    }
    if(dmx512_devp->recv_len < size)
    {
        len = dmx512_devp->recv_len;
    }
    else
    {    
        len = size;    
    }
    
    if(copy_to_user(buf,dmx512_devp->dmx_buff, len))
        ret = -EFAULT;
    else{
        ret = len;
    }
    //printk("%s end\n",__func__);
    return ret;
    
}
static ssize_t dmx512drv_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{

    return 0;
}
static int dmx512drv_close (struct inode *inodp, struct file *filp)
{
    //printk("%s\n",__func__);
    return 0;

}
static int dmx512drv_open (struct inode *inodp, struct file *filp)
{
    //printk("%s\n",__func__);
    return 0;
}

static const struct file_operations dmx512drv_fops =
{
    .owner = THIS_MODULE,
    .open =dmx512drv_open,
    .read =dmx512drv_read,
    .write =dmx512drv_write,
    .release =dmx512drv_close,
};

static int __init dmx512_init(void)
{
    int ret;
    dmx512_devp =kzalloc(sizeof(struct dmx512_uart_dev), GFP_KERNEL);
    if(!dmx512_devp)
    {
        ret = -ENOMEM;
        return ret;
    }
#if 0    
    /*动态申请dev*/
    ret = alloc_chrdev_region(&dmx512_devp->dev,0, 1, CDEV_NAME);
    if(ret)
    {
        printk("failed to allocate char device region\n");
        return ret;
    }

    cdev_init(&dmx512_devp->cdev,&dmx512drv_fops);
    
    ret = cdev_add(&dmx512_devp->cdev,dmx512_devp->dev,1);    
    if(ret)
    {
        printk("failed to cdev_add\n");
        goto unregister_chrdev;

    }
        
    return 0;
unregister_chrdev:
    unregister_chrdev_region(dmx512_devp->dev,1);
    return ret;
#endif 
    dmx512_devp->dev_major = register_chrdev(0,"dmx512_uart_drv",&dmx512drv_fops);
    if(dmx512_devp->dev_major < 0)
    {
        printk(KERN_ERR"register_chrdev error\n");
        ret =- ENODEV;
        goto err_0;

    }
    dmx512_devp->cls = class_create(THIS_MODULE,"dmx512_cls");
    if(IS_ERR(dmx512_devp->cls))
    {
        printk(KERN_ERR"class_create error\n");
        ret = PTR_ERR(dmx512_devp->cls);
        goto err_1;
    }
    dmx512_devp->dev = device_create(dmx512_devp->cls, NULL,MKDEV(dmx512_devp->dev_major, 0),NULL,"dmx512_uart");
    if(IS_ERR(dmx512_devp->dev))
    {
        printk(KERN_ERR"device_create error\n");
        ret = PTR_ERR(dmx512_devp->dev);
        goto err_2;
    }
    return 0;

err_2:
        class_destroy(dmx512_devp->cls);
err_1:
        unregister_chrdev(dmx512_devp->dev_major,"dmx512_uart_drv");
            
err_0:
    kfree(dmx512_devp);
    return ret;

}

static void __exit  dmx512_exit(void)
{
#if 0
    cdev_del(&dmx512_devp->cdev);
    unregister_chrdev_region(dmx512_devp->dev,1);
#endif
    device_destroy(dmx512_devp->cls, MKDEV(dmx512_devp->dev_major, 0));
    class_destroy(dmx512_devp->cls);
    unregister_chrdev(dmx512_devp->dev_major,"dmx512_uart_drv");
    kfree(dmx512_devp);

}


module_init(dmx512_init);
module_exit(dmx512_exit);
MODULE_LICENSE("GPL");


/*dmx512_uart.h 头文件*/
#ifndef _DMX512_UART_H_
#define _DMX512_UART_H_

#define DMX512_BUF_LEN (4096+1+3)
struct dmx512_uart_dev
{
    unsigned int dev_major;
    struct class *cls;
    struct device *dev;
    int recv_len;
    int r_size;
    bool start_read_flag;
    bool end_read_flag;
    unsigned char num_break;
    unsigned char dmx_buff[DMX512_BUF_LEN];
};

extern struct dmx512_uart_dev *dmx512_devp;

#endif /*_DMX512_UART_H_*/

 

2、串口接收中断处理函数中根据全局变量标识开始读取数据。

通过对寄存器LSR 的UART_LSR_FE位进行判断,为新的一帧的开始和结束。

通过对内核源码的分析找到uart的串口中断接收处理函数。在

sunxi-uart.c -》static unsigned int sw_uart_handle_rx(struct sw_uart_port *sw_uport, unsigned int lsr)

static unsigned int sw_uart_handle_rx(struct sw_uart_port *sw_uport, unsigned int lsr)
{
    unsigned char ch = 0;
    int max_count = 256;
    char flag;

#if IS_ENABLED(CONFIG_SERIAL_SUNXI_DMA)
    if ((sw_uport->dma->use_dma & RX_DMA)) {
        if (lsr & SUNXI_UART_LSR_RXFIFOE) {
            dev_info(sw_uport->port.dev, "error:lsr=0x%x\n", lsr);
            lsr = serial_in(&sw_uport->port, SUNXI_UART_LSR);
            return lsr;
        }
    }
#endif

    if(lsr & SUNXI_UART_LSR_FE)
    {
        if((dmx512_devp->start_read_flag) && (strncmp(sw_uport->name,"uart1",5) ==0))  /*现在用的是uart1 不同的端口需要调整,也可以通过驱动直接传过来*/
        {
            dmx512_devp->num_break++;
            if(dmx512_devp->num_break ==1)
                dmx512_devp->recv_len =0;
        }
    }
    do {

        if((dmx512_devp->start_read_flag) && (strncmp(sw_uport->name,"uart1",5) ==0))
        {
                if((lsr & SUNXI_UART_LSR_FE) &&(max_count !=256))
                        dmx512_devp->num_break++;    
        }
        
        if (likely(lsr & SUNXI_UART_LSR_DR)) {
            ch = serial_in(&sw_uport->port, SUNXI_UART_RBR);
#if IS_ENABLED(CONFIG_SW_UART_DUMP_DATA)
            sw_uport->dump_buff[sw_uport->dump_len++] = ch;
#endif
        } else
            ch = 0;

        flag = TTY_NORMAL;
        sw_uport->port.icount.rx++;
        if (unlikely(lsr & SUNXI_UART_LSR_BRK_ERROR_BITS)) {
            /*
             * For statistics only
             */
            if (lsr & SUNXI_UART_LSR_BI) {
                lsr &= ~(SUNXI_UART_LSR_FE | SUNXI_UART_LSR_PE);
                sw_uport->port.icount.brk++;

                /*
                 * We do the SysRQ and SAK checking
                 * here because otherwise the break
                 * may get masked by ignore_status_mask
                 * or read_status_mask.
                 */
                if (!ch && uart_handle_break(&sw_uport->port))
                    goto ignore_char;
            } else if (lsr & SUNXI_UART_LSR_PE)
                sw_uport->port.icount.parity++;
            else if (lsr & SUNXI_UART_LSR_FE)
                sw_uport->port.icount.frame++;
            if (lsr & SUNXI_UART_LSR_OE)
                sw_uport->port.icount.overrun++;

            /*
             * Mask off conditions which should be ignored.
             */
            lsr &= sw_uport->port.read_status_mask;
#if IS_ENABLED(CONFIG_SERIAL_SUNXI_CONSOLE)
            if (sw_is_console_port(&sw_uport->port)) {
                /* Recover the break flag from console xmit */
                lsr |= sw_uport->lsr_break_flag;
            }
#endif
            if (lsr & SUNXI_UART_LSR_BI)
                flag = TTY_BREAK;
            else if (lsr & SUNXI_UART_LSR_PE)
                flag = TTY_PARITY;
            else if (lsr & SUNXI_UART_LSR_FE)
                flag = TTY_FRAME;
        }
        if (uart_handle_sysrq_char(&sw_uport->port, ch))
            goto ignore_char;
        
        //printk("sw_uport->name =%s\n",sw_uport->name);
        /*增加对break的判断*/
  
        if((dmx512_devp->start_read_flag) && (strncmp(sw_uport->name,"uart1",5) ==0))
        {    
            if(dmx512_devp->num_break ==1)
            {
                dmx512_devp->dmx_buff[dmx512_devp->recv_len] =ch;
                dmx512_devp->recv_len++;
                if(dmx512_devp->recv_len >= dmx512_devp->r_size)
                {
                    dmx512_devp->start_read_flag = false;
                    dmx512_devp->end_read_flag = true;
     
                }
            }
            else if(dmx512_devp->num_break > 1)
            {
                    dmx512_devp->start_read_flag = false;
                    dmx512_devp->end_read_flag = true;
           
            }
        }
        
        uart_insert_char(&sw_uport->port, lsr, SUNXI_UART_LSR_OE, ch, flag);
ignore_char:
        lsr = serial_in(&sw_uport->port, SUNXI_UART_LSR);
    } while ((lsr & (SUNXI_UART_LSR_DR | SUNXI_UART_LSR_BI)) && (max_count-- > 0));

    SERIAL_DUMP(sw_uport, "Rx");
    spin_unlock(&sw_uport->port.lock);
    tty_flip_buffer_push(&sw_uport->port.state->port);
    spin_lock(&sw_uport->port.lock);

    return lsr;
}

 

3、写应用程序进行验证。

打开设置串口uart1 波特率250000 8 N 2

 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <termios.h>
#include <errno.h>
#include <signal.h>

#include <stdbool.h>


#define UART1_DEV_NAME  "/dev/ttyS1"  /*需根据实际端口修改*/
#define DMX512_DEV_NAME "/dev/dmx512_uart"
#define BUF_LEN 100
#define MAX_BUF 2048


int oflags =0;
int fd =-1;
char buff[MAX_BUF] ={0};



/**
*@brief  配置串口
*@param  fd:串口文件描述符. 
         nSpeed:波特率,
         nBits:数据位 7 or 8, 
         nEvent:奇偶校验位,
         nStop:停止位
*@return 失败返回-1;成功返回0;
*/

int set_serial(int fd, int nSpeed, int nBits, char nEvent, int nStop)
{
    struct termios newttys1, oldttys1;

    /*保存原有串口配置*/
    if(tcgetattr(fd, &oldttys1) != 0)
    {
        perror("Setupserial 1");
        return - 1;
    }
    memset(&newttys1, 0, sizeof(newttys1));
    //memcpy(&newttys1, &oldttys1, sizeof(newttys1));
    /*CREAD 开启串行数据接收,CLOCAL并打开本地连接模式*/
    newttys1.c_cflag |= (CLOCAL | CREAD);

    newttys1.c_cflag &=~CSIZE; /*设置数据位*/
    switch(nBits)    /*数据位选择*/
    {
        case 7:
            newttys1.c_cflag |= CS7;
            break;
        case 8:
            newttys1.c_cflag |= CS8;
            break;
        default:break;
    }
    
    switch(nEvent)  /*奇偶校验位*/
    {
        case '0':
            newttys1.c_cflag |= PARENB; /*开启奇偶校验*/
            newttys1.c_iflag |= (INPCK | ISTRIP); /*INPCK打开输入奇偶校验,ISTRIP 去除字符的第八个比特*/
            newttys1.c_cflag |= PARODD; /*启动奇校验(默认为偶校验)*/
            break;
        case 'E':
            newttys1.c_cflag |= PARENB; /*开启奇偶校验*/
            newttys1.c_iflag |= (INPCK | ISTRIP); /*INPCK打开输入奇偶校验,ISTRIP 去除字符的第八个比特*/
            newttys1.c_cflag &= ~PARODD; /*启动偶校验*/
            break;
        case 'N':
            newttys1.c_cflag &= ~PARENB; /*无奇偶校验*/
            break;
        default:break;
    }
    
    switch(nSpeed) /*设置波特率*/
    {
        case 2400:
            cfsetispeed(&newttys1, B2400);
            cfsetospeed(&newttys1, B2400);
            break;
        case 4800:
            cfsetispeed(&newttys1, B4800);
            cfsetospeed(&newttys1, B4800);
            break;
        case 9600:
            cfsetispeed(&newttys1, B9600);
            cfsetospeed(&newttys1, B9600);
            break;
        case 115200:
            cfsetispeed(&newttys1, B115200);
            cfsetospeed(&newttys1, B115200);
            break;
        case 250000:
            //ret = cfsetispeed(&newttys1, 0020001);
            //printf("reti = %d\n",ret);
            //ret = cfsetospeed(&newttys1, 0020001);        
            //printf("reto = %d\n",ret);
            newttys1.c_cflag |= 0020001;
            break;
        default :
            cfsetispeed(&newttys1, B9600);
            cfsetospeed(&newttys1, B9600);
            break;
    }
    
    /*设置停止位*/
    /*停止位为1,则清除CSTOPB,如停止位为2,则激活CSTOPB*/
    if(nStop == 1)
    {
        newttys1.c_cflag &= ~CSTOPB;  /*默认为停止位1*/
    }
    else if(nStop == 2)
    {
        newttys1.c_cflag |= CSTOPB;
    }

    newttys1.c_iflag &=~(PARMRK); /*不设置的*/

    newttys1.c_iflag |= IGNBRK ; /*设置的*/
    printf("newttys1.c_iflag= 0x%\n",newttys1.c_iflag);


    /*设置最少字符和等待时间,对于接收字符和等待时间没有特别的要求时*/
    newttys1.c_cc[VTIME] = 0; /*非规范模式读取时的超时时间*/
    newttys1.c_cc[VMIN] = 0; /*非规范模式读取时的最小字符数*/
    
    /*tcflush 清空终端未完成的输入、输出请求及数据
    TCIFLUSH表示清空正接收到的数据,且不读取出来*/
    tcflush(fd, TCIFLUSH);

    /*激活配置使其生效*/
    if((tcsetattr(fd, TCSANOW, &newttys1)) != 0)
    {
        perror("usart set error");
        return - 1;
    }

    return 0;
}

int main(int argc,char const * argv[])
{

    int ret =-1;
    int i =0;
    int n =0;
    int len = BUF_LEN;
    int baud = 250000;
    int fd_dmx512 =-1;

    struct sigaction saio;
    
    if(argc !=2)
    {
        printf("arg is not 2,arg is app baud_rate\n");
    }
    if(argc == 2)
        baud = atoi(argv[1]);
    printf("baud =%d\n",baud);
    fd = open(UART1_DEV_NAME, O_RDWR | O_NOCTTY | O_NDELAY);
    if(fd < 0)
    {
        perror("Can't open uart1 port");
        return(void *)"uart1 dev error";
    }
    ret = set_serial(fd,baud, 8, 'N', 2); /*可能需要根据情况调整*/
    if(ret < 0)
    {
        printf("set_serial error\n");
        return -1;    
    }

    while(1)
    {
        fd_dmx512 =open(DMX512_DEV_NAME,O_RDONLY);
        if(fd_dmx512 < 0)
        {
            printf("open dmx512 device error\n");
            return -1;
        }
       memset(buff,0,sizeof(buff));
        printf("Read start\n");
        n = read(fd_dmx512,buff,600);
        printf("Read end\n");
        printf("num=%d :",n);
        for(i=0;i<n;i++)
            printf("%02x ",buff[i]);
        printf("\n");
    
        ret = close(fd_dmx512);
        if(ret < 0)
            printf("close error\n");
    
        sleep(5);
    }

    return 0;
}

 

通过测试后正常读取到串口数据

 

 

 

 

DMX512协议解析

(1)采用RS-485总线收发器,差分电压进行传输的,抗干扰能力强,信号可以进行长距离传输;
(2)不论调光数据是否需要改变,主机都必须发送控制信号。
(3)由于数据帧之间的时间小于1s,所以在1s内没有收到新的数据帧,说明信号已经丢失;
(4)因为是数据是调光用的,使用环境是不做安全要求的设备, 并且是不间断传输的,所以不需要复杂的校验。

dmx512协议串口波特率为250000

一个bit位 4us
8个位(Slot:x) 4*8=32us,x是从1到512

 

break 88us(范围是88μs——1ms)
MAB(Mark After Break) 8us 两个bit位的时间,高电平
start bit 4us 是低电平
Start Code(SC) 32us,8个位,是一段低电平,必须要有,串口表现中数据是0,接收时作头的一部分
stop 8us 两位结束,是高电平
MTBP 0-1s(MARK Time aftet slot,每一个数据间隔的空闲时间,是高电平,可以不要。

一帧数据包括 start + Slotx: + stop + MTBP = 4+32+8+0=44us

 

参考文档

(19条消息) DMX512协议解析_春风得意吃火锅的博客-CSDN博客_dmx512协议标准

(19条消息) tty驱动 read 过程梳理_0x460的博客-CSDN博客

本文转载于网络 如有侵权请联系删除

相关文章

  • VMProtect 3.x- 如何对vmp静态分析(3)

    图4。如前所述,VMProtect2使用XOR操作解密并随后加密推送到堆栈上的相关虚拟地址。特定加密相对虚拟地址的选择是通过移动给定标志使其值为0或8来完成的。然后,添加VSP到结果移位计算加密的相对虚拟地址所在的地址。#defineFIRST_CONSTANTa60934c9 #defineSECOND_CONSTANT59f6cb36 unsignedintjcc_decrypt(unsignedintencrypted_rva) { unsignedintresult=~encrypted_rva&~encrypted_rva; result=~result&~FIRST_CONSTANT; result=~(~encrypted_rva&~SECOND_CONSTANT)&~result; returnresult; }复制图5.注意:注意FIRST_CONSTANT和SECOND_CONSTANT互为倒数。VMAssembler-概述VMAssembler-汇编器阶段VMAssembler-第一阶段,词法分析和解析VMAssembler-第二

  • 单链表的冒泡,快排,选择,插入,归并5种排序算法详解(多图+代码实现)

    上节介绍了链表的基本操作史上最全单链表的增删改查反转等操作汇总以及5种排序算法(C语言)https://cloud.tencent.com/developer/article/1826524 这节介绍链表的5种排序算法。 0.稳定排序和原地排序的定义稳定排序:   假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。像冒泡排序,插入排序,基数排序,归并排序等都是稳定排序 原地排序:   基本上不需要额外辅助的的空间,允许少量额外的辅助变量进行的排序。就是在原来的排序数组中比较和交换的排序。像选择排序,插入排序,希尔排序,快速排序,堆排序等都会有一项比较且交换操作(swap(i,j))的逻辑在其中,因此他们都是属于原地(原址、就地)排序,而合并排序,计数排序,基数排序等不是原地排序1.冒泡排序基本思想:   把第一个元素与第二个元素比较,如果第一个比第二个大,则交换他们的位置。接着继续比较第二个与第三个元素,如果第二个

  • Google Analytics:GA1到GA4

    GoogleAnalytics是谷歌外部收购而来的,在2005年的时候收购了Urchin,Urchin就是GoogleAnalytics的前身,而Urchin的前身是QuantifiedSystems,它的发展历程大概如下图:GA1是ClassicAnlaytics,是在2005年的时候发布。 GA2是UniversalAnlaytics,是在2012年的时候发布。 GA3是GlobalSiteTag,是在2017年的时候发布,这个是一个应急的版本,因为该年苹果推出ITP,对第三方cookie下手,所以谷歌推出GlobalSiteTag,让Adwords,DoubleClick,和GoogleAnalytics都用同一套写法,避免受到影响。 GA4是GoogleAnlaytics4,是2020年的时候发布,为了替代在2020年9月就停止服务的GoogleAnalyticsforAPPP,也是有些临时顶替的节奏,在推出前两个月,更新特别频繁。还有另外一种划分方法,是根据加载的js。 GA1用的js是urchin.js,这是2005年的收购Urchin后直接改名为UrchinFrom

  • C语言和C++中的字符串(string)

    参考链接:C++strcoll()知识内容:  1.C\C++字符串简述  2.C字符串相关操作  3.C++string类相关操作  一、C\C++字符串简述  1.C语言字符串  C语言字符串是字符的数组。单字节字符串顺序存放各个字符串,并用'\0'来表示字符串结束。在C语言库函数中,有一系列针对字符串的处理函数,比如说strcpy()、sprintf()、stoi()等,只能用于单字节字符串,当然也有一些函数用于处理Unicode字符串,比如wcscpy()、swprintf()等   1//C语言字符串示例  2  3#include<stdio.h> 4  5intmain() 6{ 7   chars1[20]; 8   scanf("%s",s1); 9   printf("%s\n",s1); 10   11   chars2[]="Hello,World!";12   printf("%s\n",s2);13   14   return0;15}   一般遍

  • mysql clone plugin

    mysqlcloneplugin从mysql8.0.17新增的新功能克隆分为:CloningDataLocally(本地克隆)CloningRemoteData(远程克隆)克隆可以用于数据库备份(这样就不用依赖第三方工具进行备份了)克隆可以用于搭建主从复制环境以下在mysql8.0.18的环境下操作clone插件安装1.通过配置文件加载[mysqld] plugin-load-add=mysql_clone.so clone=FORCE_PLUS_PERMANENT复制2.运行的数据库上直接installpuginINSTALLPLUGINcloneSONAME'mysql_clone.so';复制3.检查插件状态mysql>SELECTPLUGIN_NAME,PLUGIN_STATUS FROMINFORMATION_SCHEMA.PLUGINS WHEREPLUGIN_NAME='clone'; +------------------------+---------------+ |PLUGIN_NAME|PLUGIN_STATUS|

  • AWSL!这波回忆杀真的爱了!

    Hello,大家好,我是他二哥。很快就是大年三十了,身为程序员的你,在过去一年中,多多少少有些不容易吧?也许此时此刻,还有人正在和产品经理在斗智斗勇,有人正在为没有注释的代码而烦恼,有人正在为春节活动保驾护航... 没时间谈恋爱,也没时间陪家人。充斥在生活里的,大部分是加不完的班,处理不完的需求还有修改不完的bug。还记得第一次接触编程的感觉吗?那种激动、欣喜、仿佛打开了新世界的心情,一定很难忘吧?《黑客在线》、《黑客X档案》、小霸王、老式文曲星......那个时候热爱编程的你和现在的你,一定都帅炸了吧!不论如何,新的一年,要怀抱着最初的那份热爱,继续加油呀。 新的一年腾讯技术工程他二哥祝愿所有程序员:百个功能一气呵成 文档注释一应俱全几行代码搞定一切 脊柱腰椎早日康复年终奖你十月工资

  • 高并发编程学习(2)——线程通信详解

    前序文章 高并发编程学习(1)——并发基础-https://www.wmyskxz.com/2019/11/26/gao-bing-fa-bian-cheng-xue-xi-1-bing-fa-ji-chu/[1]一、经典的生产者消费者案例准备基础代码:无通信的生产者消费者我们来自己编写一个例子:一个生产者,一个消费者,并且让他们让他们使用同一个共享资源,并且我们期望的是生产者生产一条放到共享资源中,消费者就会对应地消费一条。我们先来模拟一个简单的共享资源对象:publicclassShareResource{ privateStringname; privateStringgender; /** *模拟生产者向共享资源对象中存储数据 * *@paramname *@paramgender */ publicvoidpush(Stringname,Stringgender){ this.name=name; this.gender=gender; } /** *模拟消费者从共享资源中取出数据 */ publicvoidpopup(){ System.out.println(thi

  • 路由器工作原理与实践

    路由器:通过路由为数据包选择最佳的路径。路由表:存放数据到达目的端的路由信息。路由表的形成直连网段:自动记录到路由表中。非直连网段:中间经过1个或多个路由器。需手动添加路由至路由表中。静态路由:网络规模不大,拓扑结构固定。默认路由:特殊的静态路由适用于末梢网络中。减少路由表的大小。静态路由配置Router(config)#iproutenetworkmask255.255.255.0{address|interface}network:目的IP的网络地址mask:子网掩码address:到达目的地址所经过的下一跳路由器的接口地址。interface:到达目的地址所经过的下一跳路由器的接口名称。默认路由配置Router(config)#iproute0.0.0.00.0.0.0address0.0.0.00.0.0.0:任何网络address:到达目的地址所经过的下一跳路由器的接口地址。实战操作过程R1Router>enRouter#conftEnterconfigurationcommands,oneperline.EndwithCNTL/Z.Router(config)#int

  • UML 教程

    UML教程关键词:部署图,组件图,包图,类图,复合结构图,对象图,活动图,状态机图,用例图,通信图,交互概述图,时序图,时间图 简介部署图组件图包图类图复合结构图对象图活动图状态机图用例图通信图交互概述图时序图时间图UML工具更多内容简介UML图类型UML图类型如下图所示:结构式建模图结构式建模图(Structurediagrams)强调的是系统式的建模。结构图定义了一个模型的静态架构。它们通常被用来对那些构成模型的‘要素'建模,诸如:类,对象,接口和物理组件。另外,它们也被用来对元素间关联和依赖关系进行建模。 类图对象图包图组件图部署图复合结构图行为式建模图行为式建模图(Behaviordiagrams)强调系统模型中触发的事。行为图用来记录在一个模型内部,随时间的变化,模型执行的交互变化和瞬间的状态;并跟踪系统在真实环境下如何表现,以及观察系统对一个操作或事件的反应,以及它的结果。 活动图状态图用例图通信图交互概述图时序图时间图UML概念UML从来源中使用相当多的概念。我们将之定义于统一建模语言术语汇表。下面仅列代表性的概念。对于结构而言-执行者,属性,类,元件,接口,对

  • Windows服务框架与服务的编写

    从NT内核开始,服务程序已经变为一种非常重要的系统进程,一般的驻守进程和普通的程序必须在桌面登录的情况下才能运行,而许多系统的基础程序必须在用户登录桌面之前就要运行起来,而利用服务,可以很方便的实现这种功能,而且服务程序一般不予用户进行交互,可以安静的在后台执行,合理的利用服务程序可以简化我们的系统设计,比如Windows系统的日志服务,IIS服务等等。 服务程序本身是依附在某一个可执行文件之中,系统将服务安装在注册表中的HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services位置,当需要执行服务程序时,由系统的服务控制管理器在注册表中对应的位置读取服务信息,并启动对应的程序。 下面从几个方面详细说明服务程序的基本框架服务程序的框架服务程序本身也是依附在exe或者dll文件中,一般一个普通的可执行文件中可以包含一个或者多个服务,但是为了代码的维护性,一般一个程序总是只包含一个服务。 服务程序是由服务管理器负责调度,控制的,所以我们在编写服务程序的时候必须满足服务控制管理器的调度,必须包含: 1.立即调用StartServiceCtrlDi

  • 从Native到Web(六), emscripten学习笔记: SDL游戏移植尝试

    大体浏览过emscripten的那一堆demo后,心想试试移植个游戏试试,顺便体验下这项技术的实用程度首先尝试了Onescripter,因为手头上有可以编译运行的FateStayNight. 在工程配置中增加新的Platform:Emscripten,并选择编译成可以执行的html 编译提示缺少bzlib.h,增加libbz2工程(开源),注意这个就需要编译.bc库了(相当于.lib) 再编译,依赖库没问题了.提示SDL_CD相关函数都找不到.这也可以理解,web不方便访问光驱嘛.使用#ifEMSCRIPTEN宏隔离不支持的SDL特性代码 编译通过,可以运行了,不过提示找不到脚本文件.原来emscripten对于文件IO是有很大的限制的,参考: FilesystemGuide第一种比较简单粗暴,把所有依赖的文件打包成一个.data文件,载入页面时下载,下载完再运行游戏.不过这时候所有的数据都是在内存中的,所以fopen什么函数其实访问的都是内存中的虚拟文件系统.实验证明,对于FateStayNight这种上GB资源的游戏来说,这个方法会导致浏览器崩溃-_- 第二种方式应该是floh在P

  • 资源 | 基于OpenAI Gym的股票市场交易环境

    选自Github机器之心编译参与:李泽南机器学习在股票市场中的应用一直是个吸引人的研究方向,前不久瑞士金融数据顾问的《如何用Python和机器学习炒股赚钱?》引起了很多人的关注。目前,在GitHub上已经出现了基于OpenAIGym的股票市场交易环境,该项目使用Keras,支持Theano与TensorFlow,可以帮助开发者导入各类股票市场的交易数据,构建自己的长线交易模型。希望它能为你的研究提供帮助。项目地址:https://github.com/kh-kim/stock_market_reinforcement_learning概述本项目使用OpenAIGym为股票交易市场的模拟提供了一个通用环境。训练数据为每天的收盘价,收集自GoogleFinance,当然,你也可以导入其他自己希望使用的数据。另外,它还包含AndrejKarpathy曾经提到过的简单DeepQ-learning和策略梯度算法。AndrejKarpathy的博文《DeepReinforcementLearning:PongfromPixels》:http://karpathy.github.io/2016/05

  • 从智能到自主:机器人需要拥有归纳推理能力

    《第九区》导演NeilBlomkamp的新电影《超能查派》(CHAPPiE)将会在3月6日北美上映,讲述一个有缺陷的机器人CHAPPiE在人类的帮助下成为一个能思考有意识的机器人,然后CHAPPiE展开一系列的剧情。(估计把机器人换成狮子什么,也是可以顺利演下去的那种剧情吧。) 这部电影的立意与霍金、Musk反对人工智能的态度截然相反,导演认为,如果机器人是一张白纸,如果有了思考能力有了意识,还需要通过学习和经历来帮助成长。他表示,在善意的环境中,与之互动,它将会模仿和学习人类的行为,和人类友好相处。当然,这种乐观,是电影制作人的友好视角。但是实际上,会怎样呢? 一般普通人遇到人工智能这个话题,会提问类似:“如果我们创建了机器人的意识,到底哪种类型的系统适合?”“它们会不会离开地球,离开人类?”或者“它们如何优雅融入人类社会?” 当然,还有下图这种恐慌的心态,认为人工智能以火箭的速度发展,以致人类水平的人工智能到来的时候,反应不过来。 对于这些问题,加州理工学院的人工智能学者WolfgangFink博士表示,人类无法创造一个像CHAPPiE那样聪慧的机器人。很多研究人员没有考

  • 从SAP最佳业务实践看企业管理(84)-PP-149按库存生产-重复制造

    PP149按库存生产-重复制造目的:通常在生产流程符合下列标准时使用重复制造:在较长期间生产相同或类似的产品。所生产的产品不在单独定义的批中制造。而是在特定时间段根据零件期间按特定比率生产总计数量的产品。在生产中,所生产的产品始终遵循相同的机器加工顺序和工作中心处理顺序。工艺路线倾向于简单且变化不多。流程步骤业务条件业务角色事务代码预期结果后勤计划(可选)后勤计划(144)创建计划独立需求生产计划员MD61已创建产成品的独立需求操作MRP生产计划员MDBT为物料和相关的原材料需求计划订单计划表生产计划员MF50针对生产线计划物料生产覆盖范围总览生产主管MD04查询覆盖范围库存采购无QM的采购(130)备料车间主任MF60丢失从指定的存储区域转储到车间的物料重复制造反冲车间主任MFBF‘接收’半成品物料并入库,并从库存’发出’原材料后处理清单车间主任MF47为处理过的计划订单更正任何丢失的物料移动期末(工厂)一般工厂期末结算(181)通常用于通用零部件生产,采用重复制造模式,简化处理流程,满足大批量重复性生产,计划表代替生产订单,发料采用反冲方式,即,根据完工收货数量扣减库存。本节关键词

  • 日程管理 FullCalendar

    日程管理,采用著名组件FullCalendar日历插件实现 FullCalendar提供了丰富的属性设置和方法调用,开发者可以根据FullCalendar提供的API快速完成一个日历日程的开发 1.实现按月、周、日视图各方位查看 2.可以自由按日、或者时分添加与修改日程 3.可拖拽调整日程

  • JavaScript迭代器 | 8月更文挑战

    理解迭代在软件开发领域,迭代的意思就是按照顺序反复并且多次执行一段程序,在JavaScript中,计数循环就是一种最简单的迭代,直接上代码for(leti=1;i<5;i++){ console.log(i)//1234 }复制循环是迭代机制的基础,因为它可以指定迭代的次数,每次迭代执行什么操作,及多会儿停止迭代ES5新增了Array.prototype.forEach()方法,可以进行单独记录索引及通过数组对象取得值(不够理想)因为这个方法只适用于数组,且回调比较笨拙,也无法标记何使终止。letcollection=['name','age','six']; collection.forEach((item)=>console.log(item));//nameagesix复制迭代器迭代器是按需创建的一次性对象,每个迭代器都会关联一个可迭代对象,迭代器会暴露其关联可迭代对象的API任何实现iterable接口的数据结构都可以被实现iterator接口的结构进行迭代。可迭代协议实现Iterable接口(可迭代协议)

  • 2019年前端面试题

    CSS选择器有哪些? http://www.runoob.com/cssref/css-selectors.html CSS中px、em、rem的区别? px像素(Pixel)。相对长度单位。像素px是相对于显示器屏幕分辨率而言的。(引自CSS2.0手册) PX特点 1.IE无法调整那些使用px作为单位的字体大小; 2.国外的大部分网站能够调整的原因在于其使用了em或rem作为字体单位; 3.Firefox能够调整px和em,rem,但是96%以上的中国网民使用IE浏览器(或内核)。 em是相对长度单位,相对于当前对象内文本的字体尺寸,如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸。(引自CSS2.0手册) (任意浏览器的默认字体高都是16px。所有未经调整的浏览器都符合:1em=16px。那么12px=0.75em,10px=0.625em。为了简化font-size的换算,需要在css中的body选择器中声明Font-size=62.5%,这就使em值变为16px*62.5%=10px,这样12px=1.2em,10px=1em,也就是说只需要将你的原来的px

  • Swoole——创建TCP服务

    启动TCP服务代码 <?php /** *智慧公厕 */ namespaceToilet\Action; classIndexActionextendsCommonAction { publicfunctionserver(){ //创建Server对象,监听127.0.0.1:25111端口 $serv=new\Swoole\Server("127.0.0.1",25111); //监听连接进入事件 $serv->on('Connect',function($serv,$fd){ echo"Client:Connect.\n"; setlog([$fd],'','连接','toilet.log'); }); //监听数据接收事件 $serv->on('Receive',function($serv,$fd,$from_id,$data){ $serv->send($fd,"Server:".$data); setlog([$data,$fd,$from_id],'','响应','toilet.log'); }); //监听连接关闭事件 $serv-&

  • ThinkSNS+ 是如何计算字符显示长度的

    什么是ThinkSNS+   ThinkSNS(简称TS),一款全平台综合性社交系统,为国内外大中小企业和创业者提供社会化软件研发及技术解决方案,目前最新版本为ThinkSNS+、ThinkSNS+V4、ThinkSNS+【简】。 今天我们来聊一下可能很多人都会头疼的东西:显示长度。 需求是这样的,在字符的显示上,两个英文单词才占一个中文或者其他语言的显示长度。如下: ab 哈 ?复制 上面排的是两个英文字母,一个汉字,一个Emoji。你会发现,在显示上占的宽度是一致的。一些设计上为了好看也要求有这样的处理。 例如,我们的用户名需求是最多12个非单字节字符或者24个单字节字符的需求也可以混合排的需求,我们写后端不得不处理这样的验证了。 需求规则是  /^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/ 在ThinkSNS+中,为了能把这部分验证公用,所以选择使用自定义验证规则。我们先说下计算的实现思路吧! 首先,就算是mb_strlen也没法准确的获取多字节字符和单子节字符混合在一起的长度,网上有个说法,汉字占三个字节,英文数组半

  • bootstrap学习笔记--bootstrap组件

    前面已经学习了bootstrap环境搭建以及基本布局方面的知识,下面将学习下关于bootstrap的相关组件,知识点有点多。 关于bootstrap组件知识点目录: Bootstrap--代码显示 Bootstrap--表格 Bootstrap--表单 Bootstrap--按钮 Bootstrap--图片 Bootstrap--辅助类 Bootstrap--响应式实用工具 Bootstrap--字体图标 Bootstrap--下拉菜单 Bootstrap--按钮组 Bootstrap--按钮下拉菜单 Bootstrap--输入框组 Bootstrap--导航元素 Bootstrap--导航栏 Bootstrap--面包屑导航 Bootstrap--分页 Bootstrap--标签 Bootstrap--徽章 Bootstrap--超大屏幕 Bootstrap--页面标题 Bootstrap--缩略图 Bootstrap--警告 Bootstrap--进度条 Bootstrap--多媒体对象 Bootstrap--列表组 Bootstrap--面板 Bootstrap--Wells

  • 一、Linux的基础使用--登录、开关机与在线、命令的查询帮助

    目录一、Linux的基础使用1.1XWindow与命令行模式的切换1.2命令行模式下命令的执行1.3修改支持语系1.4基础命令的操作1.5几个重要的热键[Tab]、[Ctrl+C]、[Ctrl+D]1.6命令的查询与帮助1.6.1manpage1.6.2infopage1.7正确的关机方法 一、Linux的基础使用 本文基于CentOS7.x 1.1XWindow与命令行模式的切换 CentOS中图形界面是名为XWindow的窗口管理器环境,但通常服务器都是命令行界面。 通常,我们也称命令行界面为终端界面、Terminal或Console。 Linux默认情况下会提供六个终端来让用户登录,切换方式为:【Ctrl+Alt+F1~F6】。 系统会将[F1]~[F6]命名为tty1~tty6的操作接口环境。 在登录后,我们会在行头看到形如[root@hostname~]#的字符,我们可以在后面输入命令。 上面字符中的~表示用户的home目录;举例来说,root用户的home目录是/root,user的home目录是/home/user。 另外,在Linux中,默认root的提示字符为#

相关推荐

推荐阅读