CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Problem with PRINTF and/or FPRINTF on a PIC18F4550

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
xTeox



Joined: 27 Aug 2009
Posts: 10

View user's profile Send private message MSN Messenger

Problem with PRINTF and/or FPRINTF on a PIC18F4550
PostPosted: Thu Aug 27, 2009 11:26 am     Reply with quote

Hi! I'm trying do send some chars by USART, but I'm getting a strange behavior when it runs the code.
The criteria is to send one description of a menu that will be works on a console or hyperterminal and, after, the options of the menu. But every time I stack two printf or fprintf and run the program at a PIC simulation board, it just doesn't work. The LCD gains black block characters and the communication doesn't do nothing. It's like a crash, otherwise the code is compiled as normal.
Here is piece of the code, if anyone could help I should thank ya very much:

Code:
#include <CONFIG>
#include <VARS>
#include <COAD>

void main(void) {
     config();
//     printf(LCD_putc, "At:");  << Here the LCD works
//     printf(LCD_putc, "def");  << After here and under it crashes... but this isn't the point...  the actual problem is under
//     printf(LCD_putc, "asa");
//     printf(LCD_putc, "123");
     //fprintf(USART, "Escolha a opcao do menu:\n\r");  << IF TWO OF THESE ARE ONE AFTER OTHER, IT CRASHES... SEE MORE UNDER
     #ignore_warnings 203
   while(1) {
      #ignore_warnings NONE
          if (kbhit() && USART_Rx() == 'a')
               saida = menu();
          USART_Tx(atenuacao(Read_ADC(), saida));
     }
}



In COAD:

Code:
     int menu (void) {
          unsigned int i = 0;
          //fprintf(USART, "Escolha a opcao do menu:\n\r"); << IF I PUT THIS STRECH IT CRASHES LIKE I'VE COMMENTED
          do {
               if (++i==10) i=0;
               fprintf(USART, "%u - %u db\n\r", i, opcs[i]);
          } while(i);
          #ignore_warnings 201
          while (!(kbhit() && ((opc = USART_Rx()) >= 48 && opc <= 57 || opc >= 0 && opc <= 9)));
          #ignore_warnings NONE
          return tabela(opc);
     }


The configs are all rigths:
Code:
     #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, bits=8, parity=N, stream=USART)
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Aug 27, 2009 11:40 am     Reply with quote

It would be easier for us to solve if you would post a complete, compilable
test program, with the #include for the PIC, the #fuses, #use delay(),
etc., and all necessary variable declarations. It would also be easier
if you would remove all the #ignore warnings statements. Make it easy
for us to read the code. Make the program as short as possible.

Also, post your compiler version.
xTeox



Joined: 27 Aug 2009
Posts: 10

View user's profile Send private message MSN Messenger

PostPosted: Thu Aug 27, 2009 12:45 pm     Reply with quote

Sorry... here they are:

MAIN.C
Code:
#include <CONFIG>
#include <VARS>
#include <COAD>

void main(void) {
   config();
   while(1) {
      if (kbhit() && USART_Rx() == 'a')
         saida = menu();
      USART_Tx(atenuacao(Read_ADC(), saida));
   }
}


CONFIG
Code:
#ifndef __CONFIG__

   #define __CONFIG__
   #include <CONFIG.h>
   
   void config (void) {
      setup_adc_ports(AN0);
      setup_adc(ADC_CLOCK_INTERNAL);
      set_adc_channel(0);
      LCD_inicia();
   }

#endif


CONFIG.H
Code:
#ifndef _CONFIG_H_

   #define _CONFIG_H_
   #include <18F4550.h>

   // CONFIGURAÇÕES /////////////////
   #device adc = 8                 // Número de bits utilizado na conversão de [spam]ógico para digital [até 16]
   #FUSES  NOWDT                   // Desabilita o temporizador Watch Dog
   #FUSES  INTRC                   // Oscilador Interno RC
   #FUSES  NOPROTECT               // Desabilita a proteção de leitura do código
   #FUSES  NOBROWNOUT              // Desabilita o resete Brownout
   #FUSES  NOPUT                   // Desabilita o Temporazidor Power Up
   #FUSES  NOCPD                   // Desabilita a proteção da EE (EEPROM)
   #FUSES  NODEBUG                 // Desabilita o modo de debugação para ICD
   #FUSES  NOLVP                   // Desabilita a Programação por Baixa Voltagem em B3 (PIC16) ou B5 (PIC18)
   #FUSES  NOWRT                   // Desabilita a proteção de gravação de dados na memória do programa
   #FUSES  NOWRTD                  // Desabilita a proteção de gravação de dados na EEPROM
   #FUSES  IESO                    // Habilita a chave de modo interno-externo
   #FUSES  FCMEN                   // Habiita o monitoramento anti-falha do clock
   #FUSES  NOWRTC                  // Desabilita a proteção contra gravação do registro
   #FUSES  NOEBTR                  // Desabilita a proteção na memória de leitura em tabelas
   #FUSES  NOWRTB                  // Desabilita a proteção contra gravação do bloco de inicialização
   #FUSES  NOEBTRB                 // Desabilita a proteção contra leitura do bloco de inicialização
   #FUSES  NOCPB                   // Desabilita a proteção do código do bloco de inicialização
   #FUSES  NOXINST                 // Desabilita os modos: conjunto de extensão prolongado e endereçamento indexado (modo Legacy)
   #FUSES  PLL1                    // Desabilita o PLL PreScaler

   ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   #use delay(clock=8000000)       // Clock de 8 MHz
   #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, bits=8, parity=N, stream=USART)
           // Comunicação serial RS232:
           // |- Velocidade de comunicação em 9600 bits/seg
           // |- Pino transmissor definido em C6
           // |- Pino receptor definido em C7
           // |- 8 bits de comunicação
           // |- Sem paridade de bits
           // '- Stream chamada "USART"
   ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

   #include <USART>
   #include <LCD>

#endif


USART
Code:
#ifndef __USART__

   #define __USART__
   #include <USART.h>
   
   //////////////////////////////////////////////////////////////
   char USART_Rx() {                                           //
      return fgetc(USART);                                     //
   }                                                           //
   //////////////////////////////////////////////////////////////
   
   //////////////////////////////////////////////////////////////
   void USART_Tx (int _numero) {                               //
      static int stock;                                        //
      if (stock != _numero) {                                  //
         stock  = _numero;                                     //
         fprintf(USART, "Atenuado: %u\n\r", _numero);          //
      }                                                        //
      delay_ms(100);                                           //
   }                                                           //
   //////////////////////////////////////////////////////////////

#endif


USART.H
Code:
#ifndef _USART_H_

   #define _USART_H_
   
   ////////////////////////////////////////////////////////////////

   char USART_Rx ();
   void USART_Tx (int _numero);
   
   ////////////////////////////////////////////////////////////////
   
#endif


LCD
Code:
#ifndef __LCD__

   #define __LCD__
   #include <LCD.h>
   
   //////////////////////////////////////////////////////////////
   void LCD_inicia() {                                         //
      int i;                                                   //
      LCD_comando_escrever();                                  //
      LCD_pino_RS(0);                                          //
      LCD_pino_EN(0);                                          //
      delay_ms(15);                                            //
      for (i=1; i<=3; ++i) {                                   //
         LCD_envia_nibble(3);                                  //
         delay_ms(5);                                          //
      }                                                        //
      LCD_envia_nibble(2);                                     //
      for (i=0; i<=3; ++i)                                     //
         LCD_envia_byte(0, LCD_inicia_string[i]);              //
      delay_ms(2);                                             //
   }                                                           //
   //////////////////////////////////////////////////////////////
   
   //////////////////////////////////////////////////////////////
   void LCD_goto (int _x, int _y) {                            //
      if (_x > 1)    _x = LCD_linha_2;                         //
      else       _x = 0;                                       //
      _x += _y-1;                                              //
      LCD_envia_byte(0, 0x80 | _x);                            //
   }                                                           //
   //////////////////////////////////////////////////////////////
   
   //////////////////////////////////////////////////////////////
   char LCD_getc (int _x, int _y) {                            //
      char _char;                                              //
      LCD_goto(_x, _y);                                        //
      while (bit_test(LCD_ler_byte(), 7));                     //
      LCD_pino_RS(1);                                          //
      _char = LCD_ler_byte();                                  //
      LCD_pino_RS(0);                                          //
      return _char;                                            //
   }                                                           //
   //////////////////////////////////////////////////////////////
   
   //////////////////////////////////////////////////////////////
   void LCD_putc (char _char) {                                //
      switch(_char) {                                          //
         case '\f' : LCD_envia_byte(0, 1);                     //
                  delay_ms(2);            break;               //
         case '\n' : LCD_goto(2, 1);            break;         //
         case '\b' : LCD_envia_byte(0, 0x10);   break;         //
         default   : LCD_envia_byte(1, _char);   break;        //
      }                                                        //
   }                                                           //
   //////////////////////////////////////////////////////////////
   
   //////////////////////////////////////////////////////////////
   char LCD_ler_byte() {                                       //
      int _MSB, _LSB;                                          //
      LCD_comando_ler();                                       //
      LCD_pino_EN(1);                                          //
      delay_cycles(1);                                         //
      _MSB = LCD_pino_DT;                                      //
      LCD_pino_EN(0);                                          //
      delay_cycles(1);                                         //
      LCD_pino_EN(1);                                          //
      delay_us(1);                                             //
      _LSB = LCD_pino_DT;                                      //
      LCD_pino_EN(0);                                          //
      LCD_comando_escrever();                                  //
      return ((_MSB << 4) | _LSB);                             //
   }                                                           //
   //////////////////////////////////////////////////////////////
   
   //////////////////////////////////////////////////////////////
   void LCD_envia_nibble (byte _nibble) {                      //
      LCD_pino_DT(_nibble);                                    //
      delay_cycles(1);                                         //
      LCD_pino_EN(1);                                          //
      delay_us(2);                                             //
      LCD_pino_EN(0);                                          //
   }                                                           //
   //////////////////////////////////////////////////////////////
   
   //////////////////////////////////////////////////////////////
   void LCD_envia_byte (int _RS, byte _byte) {                 //
      LCD_pino_RS(0);                                          //
      while (bit_test(LCD_ler_byte(), 7));                     //
      LCD_pino_RS(_RS);                                        //
      delay_cycles(1);                                         //
      LCD_pino_EN(0);                                          //
      LCD_envia_nibble(_byte >> 4);                            //
      LCD_envia_nibble(_byte & 0xF);                           //
   }                                                           //
   //////////////////////////////////////////////////////////////
   
   //////////////////////////////////////////////////////////////
   void LCD_comando_ler() {                                    //
      LCD_pino_EN(0);                                          //
      LCD_pino_RS(0);                                          //
      LCD_pino_DT(0xF0);                                       //
   }                                                           //
   //////////////////////////////////////////////////////////////
   
   //////////////////////////////////////////////////////////////
   void LCD_comando_escrever() {                               //
      LCD_pino_EN(0);                                          //
      LCD_pino_RS(0);                                          //
      LCD_pino_DT(0x00);                                       //
   }                                                           //
   //////////////////////////////////////////////////////////////

#endif


LCD.H
Code:
#ifndef _LCD_H_

   #define _LCD_H_
   
   ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   
   void LCD_pino_EN (boolean _bit)  { output_bit(PIN_E1, _bit); }
   void LCD_pino_RS (boolean _bit)  { output_bit(PIN_E2, _bit); }
   void LCD_pino_DT (byte    _byte) { output_D  (_byte << 4);     }
   
   #define LCD_tipo   2
   #define LCD_linha_2 0x40
   
   const int LCD_inicia_string[4] = {0x20 | (LCD_tipo << 2), 0xc, 1, 6};
   
   ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   
   void LCD_inicia            ();
   void LCD_goto            (int _x, int _y);
   char LCD_getc             (int _x, int _y);
   void LCD_putc            (char _char);
   
   char LCD_ler_byte         ();
   void LCD_envia_nibble      (byte _nibble);
   void LCD_envia_byte       (boolean _RS, byte _byte);
   
   void LCD_comando_ler      ();
   void LCD_comando_escrever   ();
   
   ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#endif


VARS
Code:
#ifndef __VARS__
#define __VARS__
#include <VARS.txt>
#endif


VARS.TXT
Code:
#ifndef __VARS_TXT__

   #define __VARS_TXT__

// VARIÁVEIS /////////////////////////
                                    //
int saida  = 40;                    //
unsigned int opcs[] = {100,         //
                        10,         //
                        20,         //
                        30,         //
                        40,         //
                        50,         //
                        60,         //
                        70,         //
                        80,         //
                        90};        //
                                    //

#endif


Arrow COAD
Code:
#ifndef __COAD__

   #define __COAD__

   #include <COAD.h>

   ////////////////////////////////////////////////////////////////
   
   int atenuacao (int entrada, int saida) {
      return saida - entrada;
   }
   
   ////////////////////////////////////////////////////////////////

   int menu (void) {
      unsigned int i = 0;
      //fprintf(USART, "Escolha a opcao do menu:\n\r");  <<<<<<<<<<<<<<<< HERE >>>>>>>>>>>>>>>>
      do {
         if (++i==10) i=0;
         fprintf(USART, "%u - %u db\n\r", i, opcs[i]);
      } while(i);
      while (!(kbhit() && ((opc = USART_Rx()) >= 48 && opc <= 57 || opc >= 0 && opc <= 9)));
      return tabela(opc);
   }
   
   ////////////////////////////////////////////////////////////////

   int tabela (int opc) {
      switch (opc) {
         case  0 :
         case '0': return opcs[0];
         case  1 :
         case '1': return opcs[1];
         case  2 :
         case '2': return opcs[2];
         case  3 :
         case '3': return opcs[3];
         case  4 :
         case '4': return opcs[4];
         case  5 :
         case '5': return opcs[5];
         case  6 :
         case '6': return opcs[6];
         case  7 :
         case '7': return opcs[7];
         case  8 :
         case '8': return opcs[8];
         case  9 :
         case '9': return opcs[9];
         default:  return 40;
      }
   }
   
   ////////////////////////////////////////////////////////////////

#endif


COAD.H
Code:
#ifndef _COAD_H_

   #define _COAD_H_

   ////////////////////////////////////////////////////////////////

   static char opc;

   ////////////////////////////////////////////////////////////////
   
   int atenuacao (int  entrada, int saida);
   int menu     (void);
   int tabela     (char opc);

   ////////////////////////////////////////////////////////////////
   
#endif
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Aug 27, 2009 1:00 pm     Reply with quote

It seems that you have a problem with lcd_putc() or with fprintf().
So make a simple test program to look at these problems.

First try it with your LCD driver. If it fails, then use the Flex driver
from the CCS code library. Example:
Code:
#include <18F4550.h>
#fuses INTRC_IO,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=8000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS, stream=USART)

#include "flex_lcd.c"

//======================================
void main(void)
{
lcd_init();

printf(lcd_putc, "At:");  // Here the LCD works
printf(lcd_putc, "def");  // After here it crashes
printf(lcd_putc, "asa");
printf(lcd_putc, "123");

fprintf(USART, "Escolha a opcao do menu:\n\r"); 

while(1);
}


Also, post your compiler version.
xTeox



Joined: 27 Aug 2009
Posts: 10

View user's profile Send private message MSN Messenger

PostPosted: Thu Aug 27, 2009 1:12 pm     Reply with quote

Sorry, but the LCD problem is another thing to fix after the RS232...
On the LCD, it doesn't work if I unify the printf's into only one, like:
Code:
printf(lcd_putc, "Atenuado:");

Seems that if I try to send more than three characters at one time, it crashes exactly like the RS232 problem has I mentioned before...
But the actual problem is for RS232... on the "COAD" there is a line specified with "<< HERE >>" text.
If you can help me with this you will be so much useful as can be with LCD...

PS.: I don't know this file "flex_lcd.c" that you mentioned... it doesn't exist in my PC.

Thx 'till now!
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Aug 27, 2009 1:18 pm     Reply with quote

Post a very small test program that shows the problem. Example:
Code:
#include <18F4550.h>
#fuses INTRC_IO,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=8000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS, stream=USART)

//======================================
void main(void)
{

fprintf(USART, "Escolha a opcao do menu:\n\r"); 

while(1);
}


1. Explain what you mean by "it crashes".

2. Post your compiler version.
xTeox



Joined: 27 Aug 2009
Posts: 10

View user's profile Send private message MSN Messenger

PostPosted: Thu Aug 27, 2009 1:52 pm     Reply with quote

I'm compiling using CCS C 4.020 PCWH Compiler integrated with MPLAB IDE 8.33 and programming using PICkit 2 v2.61.
Crashes, I mean, the PIC doesn't do nothing. It freezes. The LCD comes black characteres and the communication on RS232 by any interface (like hyperterminal) simply doesn't occur.

Here is a sample code that crashes for LCD:
Code:
#include <18F4550.h>
#fuses INTRC_IO,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=8000000)

#include <LCD>

void main(void)  {
    LCD_inicia();
    printf(lcd_putc, "Atenuado:");
    while(1);
}


Doing some tests, I found the point of the error, but I don't solved yet.
The problem is at the function "LCD_inicia()"... it crashes even the LCD test than the communication test.
Something that I've configured wrong, evidentiality.
I've did that code of "LCD" file basing in a example of the compiler CCS.
If there is a error code that you saw in "LCD", just tell me please.

The includes is in previous replies.


Tx 4'll till nw


Last edited by xTeox on Thu Aug 27, 2009 2:00 pm; edited 1 time in total
xTeox



Joined: 27 Aug 2009
Posts: 10

View user's profile Send private message MSN Messenger

PostPosted: Thu Aug 27, 2009 1:57 pm     Reply with quote

On the entry code, if I retire the comment of the function "LCD_inicia()", it will just crashes.
Code:
#include <18F4550.h>
#fuses INTRC_IO,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=8000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, stream=USART)

#include <USART>
int opcs[] = {0,10,20,30,40,50,60,70,80,90};
int saida  = 40;
#include <COAD>
#include <LCD>

void main(void) {
   setup_adc_ports(AN0);
   setup_adc(ADC_CLOCK_INTERNAL);
   set_adc_channel(0);
   //LCD_inicia();
    while(1) {
      if (kbhit() && USART_Rx() == 'a')
         saida = menu();
      USART_Tx(atenuacao(Read_ADC(), saida));
   }
}

Code:
int menu (void) {
      unsigned int i = 0;
      fprintf(USART, "Escolha a opcao do menu:\n\r");
      do {
         if (++i==10) i=0;
         fprintf(USART, "%u - %u db\n\r", i, opcs[i]);
      } while(i);
      #ignore_warnings 201
      while (!(kbhit() && ((opc = USART_Rx()) >= 48 && opc <= 57 || opc >= 0 && opc <= 9)));
      #ignore_warnings NONE
      return tabela(opc);
   }
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Aug 27, 2009 2:21 pm     Reply with quote

Your code is complicated because you have many indirections. This
makes your code more difficult to understand and debug quickly. You
could write it in a lot more simple way. For example, you have this code:
Code:

if (kbhit() && USART_Rx() == 'a')
    saida = menu();
 


It could be written like this:
Code:

if (fgetc() == 'a')
    saida = menu();

This new code waits until a character is received. It then reads it.
It then compares it to 'a'. If it equals 'a', then it calls the menu() function
and puts the result in 'saida'.

Also, your code has delays and printf statements that could block
your other code from reading the UART's receiver quickly enough.
It's possible that you are getting an overrun error in the UART
receiver. This could cause your program to lock up (freeze).

Add the ERRORS parameter to your #use rs232() statement, as shown
below. If the program doesn't freeze, then the problem was caused by
an overrun error.
Quote:
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, bits=8, parity=N, stream=USART, ERRORS)
xTeox



Joined: 27 Aug 2009
Posts: 10

View user's profile Send private message MSN Messenger

PostPosted: Thu Aug 27, 2009 2:29 pm     Reply with quote

I cannot use to read the pressed key by using "get" because I cannot stop the program. This is the because I've used "kbhit".
On my concentration, my way is the better way (to me) to separe every part of the entire code.
The addition of "ERROR" on RS232 doens't work. It's still crashing.
Anyway, I'm pretty sure that the problem is on the file "LCD" and "LCD.H".
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Aug 27, 2009 2:38 pm     Reply with quote

But in your previous comment you said for me to ignore the LCD
problem and only to help you on the fprintf problem:
Quote:

If you can help me with this you will be so much useful as can be with LCD...


So, as before, I suggest that you use the Flex LCD driver.
http://www.ccsinfo.com/forum/viewtopic.php?t=24661
xTeox



Joined: 27 Aug 2009
Posts: 10

View user's profile Send private message MSN Messenger

PostPosted: Fri Aug 28, 2009 5:32 am     Reply with quote

Yes... I did... but as soon I've found the source of the errors... is in the LCD file... without the LCD my communication works properly... there's something wrong in the function "LCD_inicia()"... or maybe in the constants... the pins are rights... but the logic (of the LCD, off course), I've based on the example from the CCS compiler...
So... I guess there's nothing wrong in the communication...
I'll read the link that u forwarded...

Tx 4 ur helping me till now...
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Fri Aug 28, 2009 5:49 am     Reply with quote

Quote:
I'm compiling using CCS C 4.020 PCWH Compiler
This is where I stopped reading.
v3.249 was a stable version. Then the v4 releases came and it took to about v4.060 to become more or less stable. Using any version in between is a waste of time; the code will not compile or will behave with strange errors. See also the sticky thread on top of this forum.

Revert to v3.249 or upgrade to a newer version.
xTeox



Joined: 27 Aug 2009
Posts: 10

View user's profile Send private message MSN Messenger

PostPosted: Fri Aug 28, 2009 6:42 am     Reply with quote

Tx ∞one...
I've found the solution... the problem is really in the LCD file... the logic isn't wrong... but, comparing mine and the example of the link, I've saw one mistake I did: as my LCD configuration doesn't use any pin to RW, because it's grounded, I don't need some specifications given from it... like:
Code:
while (bit_test(LCD_ler_byte(), 7));
...and some delays...
Removing them all, it works properly...

Tx again!!!

If I have any new problem, off course I'll ask your help, PCM Programmer!!...

Have a nice weekend!
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group