浏览量: 260 次浏览

STM32 串口通信、I2C与GPIO的应用

2020年2月1日 0 作者 Nie Hen

项目目标

  1. 通过按键,按下的值能够串口接受并显示出来,
  2. 串口发送数字,使用数码管显示数字。
  3. 将串口发送的数字使用I2C写入EEPROM,按复位键并能够读出来。
    效果演示

使用模块

串口通信 I2C 键盘 数码管
查看原理图,找到相应的引脚 这个可以参考前面的文章
每个模块都单独讲了,这个项目综合功能使用

CubeMx配置

  1. 将数码管和键盘对应的GPIO口进行相应的设置
    键盘使用扫描法 行和列 分别设为input 和output 数码管设为output
    enter description here
  2. 使用USART1 并进行相应的配置
    enter description here
    NVIC Settings处 进行使能 ENable选中 打开中断
  3. I2C配置
    配置时钟 设置两个引脚 设置I2C参数
    (这个地方图参考 STM32 I2C介绍及cubemx配置)

代码设计

  1. 添加数码管 和键盘 封装好的代码
struct GPIO_PACK{ 
        GPIO_TypeDef * port;
        uint16_t pin;
    }; //数码管 引脚对应的结构数组 
    struct GPIO_PACK segs[8]= { 
    {GPIOA,GPIO_PIN_15},
    {GPIOA,GPIO_PIN_11},
    {GPIOC,GPIO_PIN_9},
    {GPIOC,GPIO_PIN_7},
    {GPIOC,GPIO_PIN_8},
    {GPIOA,GPIO_PIN_12},
    {GPIOA,GPIO_PIN_8},
    {GPIOC,GPIO_PIN_6},
    }   ;
    struct GPIO_PACK bits[4]={
    {GPIOD,GPIO_PIN_2},
    {GPIOB,GPIO_PIN_4},
    {GPIOB,GPIO_PIN_6},
    {GPIOB,GPIO_PIN_7},

    };
uint8_t shuzu[]={  //段码 
    0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,
    0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e
};
void DisplayOnebit(uint8_t digtal,uint8_t bit){
        uint8_t i;
        for(i=0;i<8;i++){
            HAL_GPIO_WritePin(segs[i].port,segs[i].pin,(GPIO_PinState)(shuzu[digtal]&(0x01<<i)));
        }
        for(i=0;i<4;i++){
            HAL_GPIO_WritePin(bits[i].port,bits[i].pin,(GPIO_PinState)(i!=bit));
        }
}
void DisplayDigtal(uint16_t digtal){  // 调用这个函数使用数码管
    int a=0;
    DisplayOnebit(digtal%10,0);
    HAL_Delay(5);
    if (digtal>=10){
    DisplayOnebit(digtal/10%10,1);
    HAL_Delay(5);
    if (digtal>=100){
    DisplayOnebit(digtal/100%10,2);
    HAL_Delay(5);
    if (digtal>=1000){
    DisplayOnebit(digtal/1000%10,3);
    HAL_Delay(5);

                }
            }
        }
}
// 扫描键盘 
struct GPIO_PACK lie[4]={   
   {GPIOB,GPIO_PIN_0},
   {GPIOB,GPIO_PIN_1},
   {GPIOB,GPIO_PIN_13},
   {GPIOB,GPIO_PIN_12}
};
struct GPIO_PACK hang[4]={
   {GPIOA,GPIO_PIN_6},
   {GPIOA,GPIO_PIN_7},
   {GPIOC,GPIO_PIN_4},
   {GPIOC,GPIO_PIN_5}
};
  1. 一些变量声明
uint8_t connctt2[25]="The Key is "; 
uint8_t *receive;  
uint8_t receive_str[4]="0";  // 串口发送接收到的值
int number=0;
int receive_kye;  //扫描键盘获取到的值
int get_key ; //串口发送的值 转换为int
int getkey_length=0; // 获取到的值的长度
  1. 由于 按键值是int性 发送到串口是str性,从串口接收是str性,给数码管展示是int性。
    所以需要int和str来回转换的代码
void Int2Str(uint8_t* str, int intnum)   // 将无符号整形整数intnum 转换成字符 str
{
    int i, Div = 1000000000, j = 0, Status = 0;
    for (i = 0; i < 10; i++)
    {
        str[j++] = (intnum / Div) + '0';
        intnum = intnum % Div;
        Div /= 10;
        if ((str[j-1] == '0') & (Status == 0))
        {
            j = 0;
        }
        else
        {
            Status++;
        }
    }
}
int str2int1( char* str)  // 字符串转整形 
{
    int temp = 0;
    const char* p = str;
    if(str == NULL) return 0;
    if(*str == '-' || *str == '+')
    {
        str ++;
    }
    while( *str != 0)
    {
        if( *str < '0' || *str > '9')
        {
            break;
        }
        temp = temp*10 +(*str -'0');
        str ++;
    }
    if(*p == '-')
    {
        temp = -temp;
    }
    return temp;
}
  1. 扫描键盘获取到的键值直接进行处理 使用 中断发送 到串口
    (扫描法键盘 讲过 下面的处理有注释)
void Displaykeying()
    {
        uint8_t i,j,s,key=0;uint8_t k=1;
        for(i=0;i<4;i++)
    {    HAL_GPIO_WritePin(lie[i].port,lie[i].pin,GPIO_PIN_RESET);  
        for(s=0;s<4;s++){
        if(i!=s)
            HAL_GPIO_WritePin(lie[s].port,lie[s].pin,GPIO_PIN_SET);}
        for(j=0;j<4;j++)
    {   k=HAL_GPIO_ReadPin(hang[j].port,hang[j].pin);
        if(k==0)
            HAL_Delay(5);
            if(k==0)
            switch(j)
            {
                case 0:key=j+1+i;break;
                case 1:key=j+4+i;break;
                case 2:key=j+7+i;break;
                case 3:key=j+10+i;break;
            }               
        }       
    }   
    if (key !=0)
    {
        number = 1;
        uint8_t key_str[5];  // 按下的键值 转换为字符型
        //connctt2 ="The Key is ";
        strcpy(connctt2,"The Key is ");
        DisplayDigtal(key);
        Int2Str(key_str,key);  //把receive_key 按下的键值 转换为字符型key_str
        strncat(connctt2,key_str,10);  //把key_str 添加到connctt2后面 
        HAL_Delay(500);

        HAL_UART_Transmit_IT(&huart1,connctt2,25);// 串口发送 值
    }else
    number = 0;
    }

上面的都放到主函数上面 方便调用
5. 从串口接收到值 使用中断 对接收到的值处理
callback函数 (一般写在主函数 后面)

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{

    //getkey_length = 4-huart1.RxXferCount;
    uint8_t received[4];
        for(int i=0;i<4;i++)
        received[i]=receive_str[i];
    HAL_I2C_Mem_Write(&hi2c2, ADDR_AT24C02_Write, 0, I2C_MEMADD_SIZE_8BIT,received,4, 10000);
    HAL_UART_Transmit(&huart1,received,4,50);
  HAL_UART_Receive_IT(&huart1, receive_str,4);

}

接收到值以后 先使用I2C写入 ,并使用串口发送 显示出来
最后开启下一个中断接受
6. while循环中 处理的事情
一直扫描键盘调用扫描键盘的函数,能够完成任务1,
将串口中断获取到的值转换为int型,并调用数码管进行展示,完成任务2

        Displaykeying();
        get_key = str2int1(receive_str);
        DisplayDigtal(get_key);
  1. I2C读数据
    定义在外面 读和写的起始位置
#define ADDR_AT24C02_Write 0xA0
#define ADDR_AT24C02_Read 0xA1

#define BufferSize 0x100

读取EEPROM中的值,应该放在主函数中,while循环上面,每次开机能够读取到,并只读一次

HAL_I2C_Mem_Read(&hi2c2, ADDR_AT24C02_Read,0,I2C_MEMADD_SIZE_8BIT,receive_str,4,10000);

在获取到串口发送的值 就会写入到EEPROM,在callback函数中。

完整代码以及工程

芯片不同 ,对引脚的处理不同,这个需要注意
但是方法都是一样的,代码的处理思路都可以参考
GitHub链接