donatello1996

  • 2019-01-16
  • 发表了主题帖: 【Nucleo G071评测】串口1空闲中断+DMA实现不定长接收

        经过两天一夜的摸索,总算是搞清楚了G0系列型号的DMA的脾气,可以进行我接下来的串口不定长接收的DEMO。     G0系列的DMA只有一个外设DMA1,DMA1支持7个通道,这7个通道也是非常人性化地可以被任意分配到任何支持DMA传输的外设如串口、ADC、SPI接口等,这点我相信ST是在向NXP新出的RT1050系列学习。也就是说,串口1的RX接收DMA通道,可以是DMA1通道1,也可以是DMA1通道2,甚至可以是DMA1的通道7。由于板上的LPUART1外设已经用作调试打印了,所以我使用串口1即PA9 PA10用来外接串口模块做实验。     首先打开CubeMX,配置串口1以及DMA,如图,可以选择7个通道里面的任意一个,我选了DMA1通道1:     其它参数不变,像FIFO那个就不打开:     在生成的例程中,有一句语句非常重要,由于现在G0系列的DMA没有指定特定外设用特定通道,因此还需要一个DMAMUX控制器用来重映射通道,通俗来讲就是锁定通道,相关配置为DMA_InitTypeDef的Request成员变量,就是这个变量,昨晚折腾了我一晚上,因为CubeMX生成的例程里面默认是没有指定Request变量的:     然后是直接跟随官方配置即可,添加空闲中断检测与响应: void UART1_Init(int baud) {         __HAL_RCC_GPIOA_CLK_ENABLE();   __HAL_RCC_USART1_CLK_ENABLE();         __HAL_RCC_DMA1_CLK_ENABLE();                 GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;   GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;   GPIO_InitStruct.Pull = GPIO_PULLUP;   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;   GPIO_InitStruct.Alternate = GPIO_AF1_USART1;   HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);           huart1.Instance = USART1;   huart1.Init.BaudRate = baud;   huart1.Init.WordLength = UART_WORDLENGTH_8B;   huart1.Init.StopBits = UART_STOPBITS_1;   huart1.Init.Parity = UART_PARITY_NONE;   huart1.Init.Mode = UART_MODE_TX_RX;   huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;   huart1.Init.OverSampling = UART_OVERSAMPLING_16;   huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;   huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;   huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;   HAL_UART_Init(&huart1);           HAL_UARTEx_SetTxFifoThreshold(&huart1, UART_TXFIFO_THRESHOLD_1_8);   HAL_UARTEx_SetRxFifoThreshold(&huart1, UART_RXFIFO_THRESHOLD_1_8);   HAL_UARTEx_DisableFifoMode(&huart1);         __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);         HAL_NVIC_SetPriority(USART1_IRQn,0,0);   HAL_NVIC_EnableIRQ(USART1_IRQn);                 hdma_usart1_rx.Instance=DMA1_Channel1;   hdma_usart1_rx.Init.Direction=DMA_PERIPH_TO_MEMORY;   hdma_usart1_rx.Init.PeriphInc=DMA_PINC_DISABLE;   hdma_usart1_rx.Init.MemInc=DMA_MINC_ENABLE;   hdma_usart1_rx.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;   hdma_usart1_rx.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE;   hdma_usart1_rx.Init.Mode=DMA_NORMAL;   hdma_usart1_rx.Init.Priority=DMA_PRIORITY_VERY_HIGH;         hdma_usart1_rx.Init.Request=DMA_REQUEST_USART1_RX;   HAL_DMA_Init(&hdma_usart1_rx);   __HAL_LINKDMA(&huart1,hdmarx,hdma_usart1_rx);         HAL_UART_Receive_DMA(&huart1,(unsigned char*)rx_buf,BUFFERSIZE); } 写好中断服务函数,并在主程序中添加循环检测中断响应标志位的函数: void USART1_IRQHandler() {         uint32_t temp;         if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE))         {                 __HAL_UART_CLEAR_IDLEFLAG(&huart1);                 HAL_UART_DMAStop(&huart1);                 temp=__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);                 printf("------%d-----\n",temp);                 rx_len=BUFFERSIZE-temp;                 recv_end_flag=1;         } } void UART_DMA_Get() {         if(recv_end_flag==1)         {                 //rx_len=0;                 recv_end_flag=0;                 printf("rx_buf=%s\n",rx_buf);         }         HAL_UART_Receive_DMA(&huart1,(unsigned char*)rx_buf,BUFFERSIZE); } 连接好硬件电路,即使用一个CH340串口模块连接板子的PA9 PA10: 看看效果:

  • 2019-01-15
  • 回复了主题帖: 下载《ADI 任意波形发生器方案》抢楼有礼啦!

  • 回复了主题帖: Maxim 利用nanoPower创新技术,致力于降低系统的静态功耗,看视频答题赢好礼!颁奖啦

    100京东卡确认,信息无误

  • 发表了主题帖: 【Nucleo G071评测】开箱&介绍&上电&工程环境搭建&点灯&串口

         收到开发板3天,周末外出了,所以现在补回帖子。      这次ST推出G071板子,确实让我眼前一亮,传统Nucleo-64板子的MiniUSB接口被取代为更通用的microUSB接口,可以接现在Nucleo-144板子的线,不过此举有人赞成有人反对,赞成的说是与时俱进,反对的说是,要么别换掉MiniUSB,要么就直接上Type-C,MiniUSB的稳定性要比microUSB高得多,之后ST怎么取舍,就拭目以待了,反正我是挺期待Type-C接口的板子的。然后就是传统Nucleo-64开发板带的一个用户LED,这次G0的板子额外多了个LD3是指示电源信息的,跟Nucleo-144一样。当然,最重要的还是主角STM32G071RB。     G0系列必须使用CubeMX5进行开发,老旧版本CubeMX4已经不支持新型号芯片了,打开CubeMX5并选择G0: 启用LPUART1(PA2 PA3)并设置波特率115200,数据位7: 由原理图看出LD4是PA5,因此初始化PA5引脚: 设置内部晶振倍频到64MHz: 在ST官网下载G0的固件包并解压: 生成基于MDK5的例程: 最后是在MDK官网下载G0的器件支持包并安装: 查看效果: 上传工程文件:

  • 2018-12-25
  • 回复了主题帖: hi,小伙伴们!这里有棵测评许愿树

    1.原子潘多拉IOT开发板 2.NXP RT1060评估板 3.ROCK PI 4 RK3399开发板

  • 回复了主题帖: (全部已安排派送,帖内可查货运单号)联想栗子开发板评测活动入围名单出炉啦~

    已确认能按时完成评测,邮寄信息无误,另外,确认收货地址能在qq或者微信端完成么,我直接跟发出板子的工作人员确认。

  • 回复了主题帖: 【树莓派3B+测评】使用USB摄像头

    我的摄像头比较便宜所以拍出来的图片比较辣鸡。。。你这个是多少钱买的?

  • 2018-12-24
  • 回复了主题帖: EEWorld开发板芯币开始啦!参与竞价赢免单!

    c100

  • 发表了主题帖: 【树莓派3B+测评】安装libjpeg库&驱动USB摄像头

    本帖最后由 donatello1996 于 2018-12-24 09:21 编辑     Linux系统中操作图像的相关函数均可直接通过libjpeg库实现,通过此库可实现BMP转JPG,BMP转FrameBuffer显示,或是FrameBuffer转BMP/JPG保存,在此次实验中,我用libjpeg库实现FrameBuffer直接转为JPG图片。 准备工作,首先下载安装libjpeg库的支持: apt-get install libjpeg8-dev 然后插上USB免驱摄像头,如果能正常驱动的话会在/dev目录下生成video开头的外设: 使用VL42库的相关函数抓取摄像头图片并保存到本地:         if(V4L2_Init((char*)"/dev/video0")!=0)         {             printf("摄像头初始化失败\n");             return 0;         }         V4l2_Grab();         Yuyv_2_RGB888(buffers,frame_buffer);         Encode_Jpeg(frame_buffer,IMAGEWIDTH,IMAGEHEIGHT,(char*)"1.jpg"); V4L2的操作头文件: #ifndef CAMERA_H #define CAMERA_H #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <linux/types.h> #include <linux/videodev2.h> #include <setjmp.h> #include "jpeglib.h" #include "lcd.h" static int fd_video; static struct  v4l2_capability  cap; //V4L2设备信息读结构体对象 struct v4l2_fmtdesc fmtdesc; struct v4l2_format fmt; //V4L2设置捕获信息读写结构体对象 struct v4l2_streamparm setfps; //V4L2设置帧数相关读写结构体对象 struct v4l2_requestbuffers req; //V4L2申请帧缓冲结构体对象 struct v4l2_buffer buf; //V4L2缓存数据结构体对象 enum v4l2_buf_type type; //V4L2采集类型结构体对象 #define  IMAGEWIDTH    640 #define  IMAGEHEIGHT   480 unsigned char frame_buffer[IMAGEWIDTH*IMAGEHEIGHT*3]; typedef struct {     void *start;     unsigned int length; } buffer; buffer *buffers; int V4L2_Init(char * filename) {     int i,ret = 0;     if ((fd_video=open(filename,O_RDWR))==-1)     {         printf("Error opening V4L interface\n");         return (0);     }     if (ioctl(fd_video,VIDIOC_QUERYCAP,&cap) == -1)     {         printf("Error opening device %s: unable to query device.\n",filename);         return (0);     }     else     {          printf("driver:\t\t%s\n",cap.driver);          printf("card:\t\t%s\n",cap.card);          printf("bus_info:\t%s\n",cap.bus_info);          printf("version:\t%d\n",cap.version);          printf("capabilities:\t%x\n",cap.capabilities);          if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE)          {             printf("Device %s: supports capture.\n",filename);         }         if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING)         {             printf("Device %s: supports streaming.\n",filename);         }     }     fmtdesc.index=0;     fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;     printf("Support format:\n");     while(ioctl(fd_video,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)     {         printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);         fmtdesc.index++;     }     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;     fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;     fmt.fmt.pix.height = IMAGEHEIGHT;     fmt.fmt.pix.width =  IMAGEWIDTH;     fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;     if(ioctl(fd_video,VIDIOC_S_FMT, &fmt) == -1)     {         printf("Unable to set format\n");         return 1;     }     if(ioctl(fd_video,VIDIOC_G_FMT, &fmt) == -1)     {         printf("Unable to get format\n");         return 2;     }     {          printf("fmt.type:\t\t%d\n",fmt.type);          printf("pix.pixelformat:\t%c%c%c%c\n",fmt.fmt.pix.pixelformat & 0xFF, (fmt.fmt.pix.pixelformat >> 8) & 0xFF, (fmt.fmt.pix.pixelformat >> 16) & 0xFF, (fmt.fmt.pix.pixelformat >> 24) & 0xFF);          printf("pix.height:\t\t%d\n",fmt.fmt.pix.height);          printf("pix.width:\t\t%d\n",fmt.fmt.pix.width);          printf("pix.field:\t\t%d\n",fmt.fmt.pix.field);     }     //set fps     ioctl(fd_video, VIDIOC_G_PARM, &setfps);     //setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;     //setfps.parm.capture.timeperframe.numerator = 30;     //setfps.parm.capture.timeperframe.denominator = 30;     req.count=4;     req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;     req.memory=V4L2_MEMORY_MMAP;     if(ioctl(fd_video,VIDIOC_REQBUFS,&req)==-1)     {         printf("request for buffers error\n");         return 3;     }     printf("init %s \t[OK]\n",filename);     return 0; } int V4l2_Grab() {     unsigned int n_buffers;     buffers =(buffer*)malloc(req.count*sizeof (*buffers));     if (!buffers)     {         printf ("Out of memory\n");         return 0;     }     for (n_buffers = 0; n_buffers < req.count; n_buffers++)     {         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;         buf.memory = V4L2_MEMORY_MMAP;         buf.index = n_buffers;         //query buffers         if (ioctl (fd_video,VIDIOC_QUERYBUF, &buf) == -1)         {             printf("query buffer error\n");             return(0);         }         buffers[n_buffers].length = buf.length;         buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ |PROT_WRITE, MAP_SHARED,         fd_video, buf.m.offset);         if (buffers[n_buffers].start == MAP_FAILED)         {             printf("buffer map error\n");             return 0;         }     }     for (n_buffers = 0; n_buffers < req.count; n_buffers++)     {         buf.index = n_buffers;         ioctl(fd_video, VIDIOC_QBUF, &buf);     }     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;     ioctl (fd_video, VIDIOC_STREAMON, &type);     ioctl(fd_video, VIDIOC_DQBUF, &buf);     printf("grab yuyv OK\n");     return 1; } int Yuyv_2_RGB888(buffer* input_buffers,unsigned char *output_buffer) {     int i,j,r1,g1,b1,r2,g2,b2;     unsigned char y1,y2,u,v;     unsigned char *pointer;     pointer =(unsigned char*)input_buffers[0].start;     for(i=0;i<IMAGEHEIGHT;i++)     {     for(j=0;j<IMAGEWIDTH/2;j++)     //每次取4个字节,也就是两个像素点,转换rgb,6个字节,还是两个像素点     {     y1 = *( pointer + (i*IMAGEWIDTH/2+j)*4);     u  = *( pointer + (i*IMAGEWIDTH/2+j)*4 + 1);     y2 = *( pointer + (i*IMAGEWIDTH/2+j)*4 + 2);     v  = *( pointer + (i*IMAGEWIDTH/2+j)*4 + 3);     r1 = y1 + 1.042*(v-128);     g1 = y1 - 0.34414*(u-128) - 0.71414*(v-128);     b1 = y1 + 1.772*(u-128);     r2 = y2 + 1.042*(v-128);     g2 = y2 - 0.34414*(u-128) - 0.71414*(v-128);     b2 = y2 + 1.772*(u-128);     if(r1>255)     r1 = 255;     else if(r1<0)     r1 = 0;     if(b1>255)     b1 = 255;     else if(b1<0)     b1 = 0;     if(g1>255)     g1 = 255;     else if(g1<0)     g1 = 0;     if(r2>255)     r2 = 255;     else if(r2<0)     r2 = 0;     if(b2>255)     b2 = 255;     else if(b2<0)     b2 = 0;     if(g2>255)     g2 = 255;     else if(g2<0)     g2 = 0;     *(output_buffer + (i*IMAGEWIDTH/2+j)*6    ) = (unsigned char)b1;     *(output_buffer + (i*IMAGEWIDTH/2+j)*6 + 1) = (unsigned char)g1;     *(output_buffer + (i*IMAGEWIDTH/2+j)*6 + 2) = (unsigned char)r1;     *(output_buffer + (i*IMAGEWIDTH/2+j)*6 + 3) = (unsigned char)b2;     *(output_buffer + (i*IMAGEWIDTH/2+j)*6 + 4) = (unsigned char)g2;     *(output_buffer + (i*IMAGEWIDTH/2+j)*6 + 5) = (unsigned char)r2;     }     }     printf("change to RGB OK \n");     free(input_buffers); } int Encode_Jpeg(unsigned char *lpbuf,int width,int height,char *output_filename) {     struct jpeg_compress_struct cinfo ;     struct jpeg_error_mgr jerr ;     JSAMPROW  row_pointer[1] ;     int row_stride ;     char *buf=NULL ;     int x ;     FILE *fptr_jpg = fopen ((char *)output_filename,"wb");     if(fptr_jpg==NULL)     {     printf("Encoder:open file failed!/n") ;      return 0;     }     cinfo.err = jpeg_std_error(&jerr);     jpeg_create_compress(&cinfo);     jpeg_stdio_dest(&cinfo, fptr_jpg);     cinfo.image_width = width;     cinfo.image_height = height;     cinfo.input_components = 3;     cinfo.in_color_space = JCS_RGB;     jpeg_set_defaults(&cinfo);     jpeg_set_quality(&cinfo, 80,1);     jpeg_start_compress(&cinfo, 1);     row_stride = width * 3;     buf=(char*)malloc(row_stride);     row_pointer[0] =(unsigned char*)buf;     while (cinfo.next_scanline < height)     {      for (x=0;x<row_stride; x+=3)     {     buf[x]   = lpbuf[x];     buf[x+1] = lpbuf[x+1];     buf[x+2] = lpbuf[x+2];     }     jpeg_write_scanlines (&cinfo, row_pointer, 1);     lpbuf += row_stride;     }     jpeg_finish_compress(&cinfo);     fclose(fptr_jpg);     jpeg_destroy_compress(&cinfo);     free(buf);     printf("save \"JPEG\"OK\n");     return 0 ; } int close_v4l2(void) {      if(fd_video!=-1)      {          close(fd_video);          return 1;      }      return 0; } #endif 由于使用了libjpeg,因此在文件编译后面要加-ljpeg选项: 执行,执行完毕之后会在工程目录下生成1.jpg图片:

  • 2018-12-23
  • 发表了主题帖: 【树莓派3B+测评】打开I2C外设接口&驱动DS3231

    本帖最后由 donatello1996 于 2018-12-24 00:05 编辑      本帖使用一个非常简单的方法用树莓派与I2C器件通信,这个器件选为I2C接口的DS3231,直接把DS3231接到I2C-1接口中即可:      首先要启用树莓派的I2C外设接口,在桌面设置或者BOOT配置文件下都可以: 然后是使用命令行工具i2cdetect检测I2C接口和I2C从机的存在: i2cdetect -y 1 i2cdump -y 1 0x68 可以看到,DS3231前七个寄存器数据就是时间数据,从0x01~0x07依次为秒 分 时 星期 日 月 年。     然后加载wiringPi库,从程序中读取时间并显示: #ifndef _DS3231_H_ #define _DS3231_H_ #include <wiringPiI2C.h> unsigned char BCD_to_BYTE(unsigned char val) //BCD转换为Byte {     return((val>>4)*10)+(val&0x0f); } unsigned char BYTE_to_BCD(unsigned char val) //Byte码转换为BCD码 {      return(((val%100)/10)<<4)|(val%10); } unsigned char fd_i2c_ds3231,hour,min,sec,day,year,month,date; void DS3231_Init(int flag,unsigned char year,unsigned char month,unsigned char date,                  unsigned char day,unsigned char hour,unsigned char min,unsigned char sec) {     fd_i2c_ds3231=wiringPiI2CSetup(0x68);     if(flag)     {         wiringPiI2CWriteReg8(fd_i2c_ds3231,6,BYTE_to_BCD(year));         wiringPiI2CWriteReg8(fd_i2c_ds3231,5,BYTE_to_BCD(month));         wiringPiI2CWriteReg8(fd_i2c_ds3231,4,BYTE_to_BCD(date));         wiringPiI2CWriteReg8(fd_i2c_ds3231,3,BYTE_to_BCD(day));         wiringPiI2CWriteReg8(fd_i2c_ds3231,2,BYTE_to_BCD(hour));         wiringPiI2CWriteReg8(fd_i2c_ds3231,1,BYTE_to_BCD(min));         wiringPiI2CWriteReg8(fd_i2c_ds3231,0,BYTE_to_BCD(sec));     } } void DS3231_Read() {     sec=wiringPiI2CReadReg8(fd_i2c_ds3231,0);     sec=BCD_to_BYTE(sec);     min=wiringPiI2CReadReg8(fd_i2c_ds3231,1);     min=BCD_to_BYTE(min);     hour=wiringPiI2CReadReg8(fd_i2c_ds3231,2);     hour=BCD_to_BYTE(hour);     day=wiringPiI2CReadReg8(fd_i2c_ds3231,3);     date=wiringPiI2CReadReg8(fd_i2c_ds3231,4);     date=BCD_to_BYTE(date);     month=wiringPiI2CReadReg8(fd_i2c_ds3231,5);     month=BCD_to_BYTE(month);     year=wiringPiI2CReadReg8(fd_i2c_ds3231,6);     year=BCD_to_BYTE(year); } #endif 看看效果:

  • 发表了主题帖: 【树莓派3B+测评】连接WIFI&登录VNC&更新系统支持

        这几天折腾了一下树莓派的WIFI,耗了一点时间,后面发现树莓派的Raspbian系统有个特点,如果锁定了树莓派的以太网IP即eth0的IP的话,WIFI就无法启用,WIFI要启用,那么eth0的IP就必须是非静态锁定的,这个BUG应该是Raspbian官网11月份更新的最新镜像里面有的,因为我以前试过老版本的镜像,是不会出现eth0静态IP与WIFI冲突的。     连接WIFI的最简单方式有两种,第一是直接接HDMI屏幕,使用右上角的WIFI按钮来连接,注意在连接WIFI之前要先选好所在国家,即CN China中国;第二是使用串口登录的方式,通过/etc/wpa_supplicant/wpa_supplicant.conf文件或者/etc/network/interfaces文件来设置WIFI热点,这里我开启手机的WIFI热点,让电脑的无线网卡和树莓派都连上: 如图所示,电脑IP为192.168.43.165,树莓派则是192.168.43.36,ping通之后可以进行SSH登录,在命令行输入 vncserver 可以打开一个端口号为1的vnc服务器: 下载VNC Viewer,输入IP,端口(即在IP后面加:1),用户名root,密码root,可进入VNC桌面: 在这可以看到刚刚连上的WIFI,注意这个WIFI是VNC服务器连接的基础,如果断掉此WIFI,那么VNC和SSH连接都会中断: 然后是切换软件源为国内的软件源,比如华中科大的软件源,即覆盖/etc/apt/sources.list文件,为什么要这样做呢,因为国外的Raspbian源下载速度太慢了: 然后就可以在VNC的命令行或者SSH的命令行中使用 apt-get update apt-get upgrade 来更新系统支持:

  • 2018-12-22
  • 发表了主题帖: 【树莓派3B+测评】线程的挂起与恢复&CPU温度检测

    本帖最后由 donatello1996 于 2018-12-22 17:33 编辑     在TCP通信中,除了线程的创建和删除以外,挂起和解挂也是非常重要的步骤,简单而言,挂起线程就是让该线程暂停执行,一直在阻塞,而解挂线程(恢复)就顾名思义了,解除挂起状态继续运行,这里我再开辟一个线程用于循环检测CPU温度,一秒检测一次,以确保系统在正常运作。读取温度的方法是读取/sys/class/thermal/thermal_zone0/temp文件的数值,将数值传输到标准文件流,再通过printf终端输出: #define TEMP_PATH "/sys/class/thermal/thermal_zone0/temp" int fd; char buf[30]; fd = open(TEMP_PATH, O_RDONLY); read(fd, buf, 30); 然后是线程的创建,除了之前的创建线程本身以外,还要创建互斥锁和cond: pthread_create(&id2,NULL,Thread_CPU_Temp,NULL);                 printf("CPU温度检测线程建立\n");                 if (pthread_mutex_init(&mut,NULL))                 {                         printf("互斥锁初始化失败\n");                 }                 if (pthread_cond_init(&cond,NULL))                 {                         printf("cond初始化失败\n");                 } 挂起和解挂其实就是对锁和cond信号量的操作: void thread_resume() {         if (status == STOP)         {                 pthread_mutex_lock(&mut);                 status = RUN;                 pthread_cond_signal(&cond);                 printf("CPU温度检测线程恢复运行\n");                 pthread_mutex_unlock(&mut);         }         else         {                 printf("CPU温度检测线程一直在运行\n");         } } void thread_pause() {         if (status == RUN)         {                 pthread_mutex_lock(&mut);                 status = STOP;                 printf("CPU温度检测线程暂停(挂起)\n");                 pthread_mutex_unlock(&mut);         }         else         {                 printf("CPU温度检测线程一直在暂停\n");         } } 操作cond信号量的时候必须锁上线程的共享资源,如果该线程挂起了,那么这个线程就一直阻塞而不执行任何操作,Linux系统在轮转执行到此线程 时间片的时候会自动跳过此线程。 完整代码如下: #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <pthread.h> #include <linux/fb.h> #include <linux/input.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/stat.h> #include <arpa/inet.h> #include <netinet/in.h> #include <wiringPi.h> #include "raspi_led_pwm.h" int fd_socket; pthread_t id1,id2; #define RUN  1 #define STOP 0 pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; unsigned char sendbuf[100],recvbuf[100]; int thread_flag=0,status=STOP; #define TEMP_PATH "/sys/class/thermal/thermal_zone0/temp" void *Thread_CPU_Temp(void *arg) {     int fd;     double temp = 0;     char buf[30];     while(1)     {         pthread_mutex_lock(&mut);         while(!status)         {             pthread_cond_wait(&cond, &mut);         }         pthread_mutex_unlock(&mut);         fd = open(TEMP_PATH, O_RDONLY);         if (fd < 0)         {             fprintf(stderr, "无法打开thermal_zone0/temp文件\n");             return -1;         }         if (read(fd, buf, 30) < 0)         {             fprintf(stderr, "读取温度数据失败\n");             return -1;         }         temp = atoi(buf) / 1000.0;         printf("%.2f\n", temp);         sleep(1);     } } void thread_resume() {         if (status == STOP)         {                 pthread_mutex_lock(&mut);                 status = RUN;                 pthread_cond_signal(&cond);                 printf("CPU温度检测线程恢复运行\n");                 pthread_mutex_unlock(&mut);         }         else         {                 printf("CPU温度检测线程一直在运行\n");         } } void thread_pause() {         if (status == RUN)         {                 pthread_mutex_lock(&mut);                 status = STOP;                 printf("CPU温度检测线程暂停(挂起)\n");                 pthread_mutex_unlock(&mut);         }         else         {                 printf("CPU温度检测线程一直在暂停\n");         } } void *Thread_Send_buf(void *arg) {     int len;     while(1)     {         bzero(sendbuf,100);         scanf("%s",sendbuf);         if(sendbuf[0]=='1')         {             if(thread_flag==0)             {                 thread_flag=1;                 pthread_create(&id2,NULL,Thread_CPU_Temp,NULL);                 printf("CPU温度检测线程建立并处于阻塞状态\n");                 if (pthread_mutex_init(&mut,NULL))                 {                         printf("互斥锁初始化失败\n");                 }                 if (pthread_cond_init(&cond,NULL))                 {                         printf("cond初始化失败\n");                 }             }         }         else if(sendbuf[0]=='2')         {             thread_pause();         }         else if(sendbuf[0]=='3')         {             thread_resume();         }         for(len=0;sendbuf[len]!='\0';len++);         send(fd_socket,sendbuf,len,0);     } } int main() {         int i=0;         int ret=-1;         wiringPiSetup();         //Raspi_LED_Init();         //Raspi_PWM_Init(100);         //pwmWrite(1,60);         /*         struct sockaddr_in sockaddr_in_comm,sockaddr_in_settings;         bzero(&sockaddr_in_settings,sizeof(sockaddr_in_settings));         sockaddr_in_settings.sin_family=AF_INET;         sockaddr_in_settings.sin_addr.s_addr=inet_addr("169.254.122.5");         sockaddr_in_settings.sin_port=htons(8087);         */         socklen_t addrsize=sizeof(struct sockaddr);         struct sockaddr_in girladdr;         bzero(&girladdr,sizeof(girladdr)); // 清零         girladdr.sin_family=AF_INET;         girladdr.sin_port=htons(10086);         girladdr.sin_addr.s_addr=inet_addr("169.254.122.1");         int thread_1=0;         while(1)         {             while(1)             {                 fd_socket=socket(AF_INET,SOCK_STREAM,0);                 if(fd_socket==-1)                 {                         printf("套接字初始化失败!\n");                         return -1;                 }                 ret=connect(fd_socket,(struct sockaddr *)&girladdr,addrsize);                 if(ret==0)                 {                     printf("与服务器建立连接\n");                     ret=pthread_create(&id1,NULL,Thread_Send_buf,NULL);                     if(ret==0)                         printf("TCP发送阻塞线程被创建\n");                     break;                 }             }             while(1)             {                 bzero(recvbuf,100);                 ret=recv(fd_socket,recvbuf,100,0);                 if(ret==0)                 {                     printf("与服务器失去连接\n");                     ret=pthread_cancel(id1);                     if(ret==0)                         printf("TCP发送阻塞线程被取消\n");                     break;                 }                 printf("服务器端发来信息:%s\n",recvbuf);             }         } } 看看效果,当输入1的时候,创建CPU温度检测线程,输入2的时候,线程挂起,输入3的时候线程恢复运行:

  • 2018-12-19
  • 发表了主题帖: 【树莓派3B+测评】TCP客户端&阻塞线程创建&取消

    本帖最后由 donatello1996 于 2018-12-19 09:30 编辑 【树莓派3B+测评】TCP客户端&阻塞线程创建&取消     在Linux系统中,TCP通信还有一个常用角色是客户端,像树莓派这种板子,经常充当从机角色连接电脑主机,在这种情况下,以客户端身份连接主机是比较易于理解且占用资源较少的做法,主机的服务器一直开着,树莓派从机可按需分时进行TCP连接或者一直检测主机服务器是否掉线以确保通信的稳定,那么,在上一帖中提到过,TCP通信要同时收发且互不干扰,必须开启多一个用于发送的线程,当TCP连接断开的时候,这个线程要挂起或者取消,在频繁的客户端连接主机的过程中,自然也就会频繁地开启或关闭TCP发送线程,这就涉及到线程里面最简单的创建和取消操作。     首先让树莓派以客户端身份连接电脑主机TCP服务器,非常简单,只需要用到socket()和connect()两个函数:         socklen_t addrsize=sizeof(struct sockaddr);         struct sockaddr_in girladdr;         bzero(&girladdr,sizeof(girladdr)); // 清零         girladdr.sin_family=AF_INET;         girladdr.sin_port=htons(10086); //host-->network         girladdr.sin_addr.s_addr=inet_addr("169.254.122.1");         connect(fd_socket,(struct sockaddr *)&girladdr,addrsize); 如果是做一个循环检测主机TCP服务器是否存在的算法,就使用三个while循环即可:         while(1)         {             while(1)             {                 fd_socket=socket(AF_INET,SOCK_STREAM,0);                 if(fd_socket==-1)                 {                         printf("套接字初始化失败!\n");                         return -1;                 }                 ret=connect(fd_socket,(struct sockaddr *)&girladdr,addrsize);                 if(ret==0)                 {                     printf("与服务器建立连接\n");                     break;                 }             }             while(1)             {                 bzero(recvbuf,100);                 ret=recv(fd_socket,recvbuf,100,0);                 if(ret==0)                 {                     printf("与服务器失去连接\n");                     break;                 }                 printf("服务器端发来信息:%s\n",recvbuf);             }         } 另外,还要开辟一个用于TCP输入字符串发送的线程,与上一帖一样: int fd_socket; pthread_t id1; unsigned char sendbuf[100],recvbuf[100]; void *Thread_Send_buf(void *arg) {     int len;     while(1)     {         bzero(sendbuf,100);         scanf("%s",sendbuf);         //printf("----\n");         for(len=0;sendbuf[len]!='\0';len++);         //printf("%d\n",Raspi_DHT11_Read());         send(fd_socket,sendbuf,len,0);     } } 要将线程的建立&取消和TCP客户端循环连接服务器的代码结合起来用,才可以实现本帖的功能: int fd_socket; pthread_t id1; unsigned char sendbuf[100],recvbuf[100]; void *Thread_Send_buf(void *arg) {     int len;     while(1)     {         bzero(sendbuf,100);         scanf("%s",sendbuf);         for(len=0;sendbuf[len]!='\0';len++);         send(fd_socket,sendbuf,len,0);     } } int main() {         int i=0;         int ret=-1;         socklen_t addrsize=sizeof(struct sockaddr);         struct sockaddr_in girladdr;         bzero(&girladdr,sizeof(girladdr)); // 清零         girladdr.sin_family=AF_INET;         girladdr.sin_port=htons(10086); //host-->network         girladdr.sin_addr.s_addr=inet_addr("169.254.122.1");         int thread_1=0;         while(1)         {             while(1)             {                 fd_socket=socket(AF_INET,SOCK_STREAM,0);                 if(fd_socket==-1)                 {                         printf("套接字初始化失败!\n");                         return -1;                 }                 ret=connect(fd_socket,(struct sockaddr *)&girladdr,addrsize);                 if(ret==0)                 {                     printf("与服务器建立连接\n");                     ret=pthread_create(&id1,NULL,Thread_Send_buf,NULL);                     if(ret==0)                         printf("TCP发送阻塞线程被创建\n");                     break;                 }             }             while(1)             {                 bzero(recvbuf,100);                 ret=recv(fd_socket,recvbuf,100,0);                 if(ret==0)                 {                     printf("与服务器失去连接\n");                     ret=pthread_cancel(id1);                     if(ret==0)                         printf("TCP发送阻塞线程被取消\n");                     break;                 }                 printf("服务器端发来信息:%s\n",recvbuf);             }         } } 看看效果,运行程序之后,树莓派会一直检测主机的TCP服务器是否开启,如果开启了就连接,并可正常使用TCP收发: 如果主机主动断开连接,那么树莓派端一样会检测到: 如果主机再次开启TCP服务器,那么树莓派会在十几秒之后重新握手建立连接: 至于为什么是十几秒呢,这个据以前的Linux大佬说,TCP端口重新开启需要十几秒的时间。 即使连接是断开之后再建立的,仍可正常收发:

  • 2018-12-17
  • 发表了主题帖: 【树莓派3B+测评】多线程&TCP服务器

    在Linux系统中TCP通信十分常见,TCP通信可以发生在同一局域网中或跨局域网的互联网之间的设备。Linux本身就支持基于C语言的TCP/IP协议通信,需要包含以下头文件: #include <linux/fb.h> #include <linux/input.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/stat.h> #include <arpa/inet.h> #include <netinet/in.h> 无论是Linux还是别的系统,要进行TCP通信都有几个步骤,在源码中以函数操作简化了,对于TCP服务器初始化,分别是socket()初始化套接字,bind()绑定,listen()监听,accept()接听:        socklen_t addrsize=sizeof(struct sockaddr);         struct sockaddr_in sockaddr_in_comm,sockaddr_in_settings;         //初始化该结构体变量         bzero(&sockaddr_in_settings,sizeof(sockaddr_in_settings));         sockaddr_in_settings.sin_family=AF_INET;         sockaddr_in_settings.sin_addr.s_addr=inet_addr("169.254.122.5");         sockaddr_in_settings.sin_port=htons(8086);         fd_socket=socket(AF_INET,SOCK_STREAM,0);         if(fd_socket==-1)         {                 printf("套接字初始化失败!\n");                 return -1;         }         ret=bind(fd_socket,(struct sockaddr *)&sockaddr_in_settings,addrsize);         if(ret==-1)         {                 printf("套接字绑定失败!\n");                 return -1;         }         ret=listen(fd_socket,5);         if(ret==-1)         {                 printf("服务器监听失败!\n");                 return -1;         }         newsock=accept(fd_socket,(struct sockaddr *)&sockaddr_in_comm,&addrsize);         if(newsock==-1)         {                 printf("服务器接听失败!\n");                 return -1;         } 当listen和accept操作完成之后,就可以使用recv和send两个函数进行TCP的接收和发送操作了,像上面的代码,开启了IP为169.254.122.5,端口为8086的服务器:         while(1)         {             bzero(recvbuf,100);             recv(newsock,recvbuf,100,0);             printf("客户端发来信息:%s\n",recvbuf);         } 注意recv函数跟scanf一样,是阻塞的,因此Linux要同时进行TCP通信接收发送的话,需要开启多线程,在main函数中使用pthread_create函数,指定函数指针,并在程序的别的地方写一个返回值为void*的函数: pthread_create(&id1,NULL,Thread_Send_buf,NULL); void *Thread_Send_buf(void *arg) {     char sendbuf[100];     while(1)     {         bzero(sendbuf,100);         scanf("%s",sendbuf);         send(newsock,sendbuf,100,0);     } } 执行程序后,使用SSCOM或者别的软件以客户端身份登录TCP服务器,并发送一些字符串,由于TCP通信传输的单位是八位字节数据,如果在接收端不做GB2312编码判断并解码的话是无法解析中文信息的,因此发送中文会显示乱码: 然后是客户端给服务器回应信息,注意此时的sendbuf数组没有做基于发送信息长度的判断,信息的长度恒定为100,后面会以'\0'填充: 所以我们再做一个不定长发送缓冲判断算法,非常简单: void *Thread_Send_buf(void *arg) {     char sendbuf[100];     int len;     while(1)     {         bzero(sendbuf,100);         scanf("%s",sendbuf);         for(len=0;sendbuf[len]!='\0';len++);         send(newsock,sendbuf,len,0);     } } 看看效果:

  • 2018-12-16
  • 发表了主题帖: 【树莓派3B+测评】PWM呼吸灯&控制12V电机

    本帖最后由 donatello1996 于 2018-12-17 01:01 编辑      wiringPi软件库中可直接操作树莓派的特定GPIO接口输出PWM波,但只能是特定支持PWM复用输出功能的GPIO接口,其它不支持PWM输出的引脚只能模拟PWM。树莓派目前有三个GPIO接口支持PWM输出,分别是GPIO1、GPIO24、GPIO25,并且同时最多只能有两路PWM波输出: 在程序中用wiringPi软件库初始化PWM引脚,做法跟STM32单片机差不多,将LED灯阳极接到GPIO24,阴极接地: #define MOTOR 1 #define RGB_LED 24 void Raspi_PWM_Init(int range) {     pinMode(RGB_LED,PWM_OUTPUT);     pinMode(MOTOR,PWM_OUTPUT);     pwmSetRange(range); } 然后是在程序中设置呼吸灯的效果算法:         while(1)         {             for(i=0;i<=100;i++)             {                 pwmWrite(RGB_LED,i);                 usleep(10*1000);             }             for(i=100;i>=0;i--)             {                 pwmWrite(RGB_LED,i);                 usleep(10*1000);             }         } 看看效果: 将L298N模块的IN1接口接GPIO1,IN2接地,OUT1和OUT2接风扇的两个极,然后在VCC端接12V供电,即可利用L298N这个大H桥做到TTL电平控制风扇转速: 在实际效果中,L298N模块要稳定控制风扇,需要设置输入PWM占空比大于40%才可以,不知道这是什么情况,不然的话风扇不转或者转速无法恒定,还请教搞直流电机的大佬们。

  • 发表了主题帖: 【树莓派3B+测评】GCC编译HelloWorld&点灯程序

    本帖最后由 donatello1996 于 2018-12-16 18:03 编辑     树莓派3B+的Raspbian系统自带了GCC编译器,编译程序非常方便,在串口登录或者命令行登录系统之后,直接敲击 gcc 或 gcc -v 命令即可验证: 使用XSHELL命令行调试助手+SSH登录非常方便哈哈。 我们先来编写一个非常简单的HelloWorld程序: 这里打印两行字符串,注意,在Linux系统中使用printf函数一定要注意,语句末尾必须加\n换行,因为在Linux系统中,在终端使用串口发送指令是以缓存块为单位的,\n是缓存结束的标志,必须加,不加的话会导致诸多BUG。 然后是做文件传输有关的工作,比较常用的方法是直接使用SFTP协议传输,比如使用FileZilla Client软件将刚刚写好的.c文件发送到开发板上: 敲击 gcc main1.c -o 1 指令编译,生成一个名称为1的可执行文件: 敲击 ./1 执行: 然后是点灯,还是熟悉的味道,用树莓派点灯还算是简单的,直接用wiringPi软件库即可实现,该软件库是直接操作系统内存的,通过读写内存块来修改BCM芯片的GPIO外设地址内容,进而实现控制GPIO的输出电平,使用wiringPi软件来操作,GPIO引脚翻转速率可达1~2MHz。由于Raspbian系统默认安装了wiringPi软件库,因此我就不介绍安装方法了,直接上指令: gpio readall 可打印引脚状态: 只需要简单几句,即可实现点灯: #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <wiringPi.h> #include "raspi_led_pwm.h" int main() {         wiringPiSetup();         Raspi_LED_Init();         while(1)         {                 digitalWrite(LED5V,HIGH);                 sleep(1);                 digitalWrite(LED5V,LOW);                 sleep(1);         } } #define LED5V 0 void Raspi_LED_Init() {     pinMode(LED5V,OUTPUT);     digitalWrite(LED5V,HIGH); } 注意在编译的时候要加上-lwiringPi: 在GPIO0引脚处连接光耦继电器,继电器另一端连接LED照明灯,即可点灯,非常简单:、

  • 2018-12-06
  • 发表了主题帖: 【树莓派3B+测评】烧录系统&锁定静态IP&SSH登录&SFTP登录

    本帖最后由 donatello1996 于 2018-12-7 00:21 编辑     收到板子很久了,可是忙于月底公司事务,一直没来得及发帖,从现在开始的一段时间内会陆续补上。     这个树莓派是之前的坛友试用回收的一块板,型号是3B+,不多做介绍,就是一块3B的升级版,CPU主频升高,网卡速度更快。     树莓派不支持flash启动和U盘启动,板子拿到手首先要准备给SD卡烧录系统,由于我的项目涉及到QT跨平台程序开发,因此下载官网的Raspbian系统,这里官网更新了一个11月新出的镜像,包含了Raspbian系统+图形化桌面+部分常用的软件: https://www.raspberrypi.org/downloads/raspbian/ 下载完成,解压得到是一个4.93GB的img镜像,好大呀: 将TF卡和读卡器插到电脑上,使用DiskGenius软件格式化TF卡,初始化为出厂状态: 然后使用Win32 DiskImager软件烧录系统到SD卡上: 烧录完成之后TF卡在Windows下只剩下一个60多MB的BOOT分区可以被识别,还有一块剩下的大分区无法被Windows识别,是EXT格式的文件系统,即Raspbian系统: 在启用系统进入桌面之前要先设置分辨率参数,不然一插上显示屏显示的话就是默认的1280*720分辨率。非常难看,在BOOT分区的config.txt文件设置分辨率参数,为1600*900的16:9宽屏,并把参数前面的#号去掉,否则不会执行: [font=微软雅黑][size=5]framebuffer_width=1600[/size][/font] [font=微软雅黑][size=5]framebuffer_height=900[/size][/font] 复制代码 将TF卡插上树莓派,连接HDMI接口和USB无线键鼠接口,并在USB供电接口处连接5V供电,需使用至少2A的供电设备: 显示启动画面,表示登录成功: 看到这个桌面,分辨率设置得不是太好,有一圈黑边,以后再慢慢设置: 打开Linux的设备,习惯性第一件事是启动命令行,sudo -s获得管理员#权限,查看当前板子的IP地址: 由于Raspbian系统是Debian系统魔改而来的,支持绝大部分的Debian命令和设置,因此可以用Debian锁定静态IP的方法,即直接修改/etc/network/interfaces文件,在其后增加语句或直接清空文件,只留下这几句: [font=微软雅黑][size=5]auto eth0[/size][/font] [font=微软雅黑][size=5]allow-hotplug eth0[/size][/font] [font=微软雅黑][size=5]iface eth0 inet static[/size][/font] [font=微软雅黑][size=5]address 169.254.122.5[/size][/font] [font=微软雅黑][size=5]#静态IP[/size][/font] [font=微软雅黑][size=5]netmask 255.255.255.0[/size][/font] [font=微软雅黑][size=5]#子网掩码[/size][/font] [font=微软雅黑][size=5]gateway 169.254.122.1[/size][/font] [font=微软雅黑][size=5]#网关[/size][/font] 复制代码 静态IP为自定义设置,我的电脑的网口IP是169.254.122.1,因此树莓派的IP必须与该IP在同一网段,即169.254.122.XXX,网关必须为169.254.122.1。 网上流传的修改/etc/dhcpcd.conf文件方法,经过实测,在树莓派3B+上根本行不通,我一晚上花了几十分钟修改这个文件,毫无卵用: 然后就是启用SSH登录功能了,Raspbian系统默认关闭ssh登录功能,首先要在TF卡的BOOT分区下新建一个名为ssh的文件,无后缀: 然后就可以使用默认的pi raspberry帐号进行SSH登录了: 注意启用ssh服务之后每次开机都会有提示: 但是实际上,这个pi帐号登录进去之后并不是默认就是管理员权限的,需要sudo -s指令开启,因此在SFTP协议下登录很不方便,因为没有管理员权限,无法解锁全部修改文件的权限(chmod 777),因此需要在/etc/ssh/sshd_config文件下修改指令启用root帐号,无密码: root帐号启用之后就可以进行无密码的SSH登录了: 可是要进行SFTP登录的话,需要给root账户设置一个密码,我这里设置的是root root,最常用的帐号密码,方法如下: sudo passwd root复制代码 输入两次密码,即可将密码保存。 使用root root账户在FileZilla Client软件下进行SFTP登录成功(第一次登录的时候会提示密钥是否保存):

  • 2018-11-28
  • 回复了主题帖: 【颁奖】新驱动力MM32开发板测评活动

    确认信息无误

  • 2018-11-25
  • 回复了主题帖: 考验眼力的时候到了

    答案是40

  • 2018-11-23
  • 回复了主题帖: 【树莓派3B+测评】远程登录

    DDZZ669 发表于 2018-11-22 14:39 putty小巧,目前不需要使用太多的功能,存在即有其合理性
    反正我觉得putty的bug挺多,不好用

最近访客

< 1/5 >

统计信息

已有99人来访过

  • 芯币:301
  • 好友:1
  • 主题:29
  • 回复:35
  • 课时:--
  • 资源:--

留言

你需要登录后才可以留言 登录 | 注册


早晨五点 2018-7-13
在吗哥,我想请教您一个问题。我用HAL库的接收中断接收数据为什么接收到的数据总是变换次序?比如第2个数据变成第一个,第三个编程第二个。。。我应该怎么办呢?希望您能帮助我一下
查看全部