Skocz do zawartości

doles

Użytkownicy
  • Zawartość

    6
  • Rejestracja

  • Ostatnio

Reputacja

0 Neutralna

O doles

  • Ranga
    2/10

Informacje

  • Płeć
    Mężczyzna
  1. Hm, dziwne bo fakt, dla PCFki pin konfiguracyjny adresu urządzenia mam zwarty do masy stąd adres 0xA0. Z kolei pin E2 pamięci eeprom mam zwarty do Vcc, czyli jest tam "1" logiczne - adresy już są inne i z tego wynika, że adres pamięci eeprom to 0xA8. Poza tym kod dla AVR z tymi adresami działał, w dodatku były jeszcze 2 inne urządzenia na magistrali.
  2. Jasne stary nie ma problemu, mogę wrzucić calutki kod, na którym się uczę. Sam się wkurzam, że do STM32F0 jest słaby support. Wszędzie tylko F1, F3 i F4. Tutaj zrobiłem Timer na zwykłym przerwaniu, przerwanie zewnętrzne, ADC z DMA, uart na przerwaniach, timer z PWM 4 kanałowym, jest zrobiony bardzo fajny i dokładny delay na timerze (us i ms) no i teraz I2C. Wklejam cały kod, na pewno komuś się przyda. Dodatkowo jest obsługa czujnika SHT11 (czujnik wilgotności i temperatury) - wszystko działa pięknie, aa no i napisałem bibliotekę również pod znany i popularny wyświetlacz oparty o sterownik HD44780. Zostawiam tutaj kod, jak ktoś będzie potrzebować plików źródłowych, proszę pisać: damiandoles@gmail.com Ze względu na fakt, że naprawdę wkurza mnie brak pomocy do F0 - a to takie fajne małe procki ! Po co do "zegarka" brać potężny kombajn z serii F3 czy F4...api jest to samo. Umiesz F0 umiesz i F3, parę drobnych różnic, ale idea podobna. Zatem może komuś pomogę kodem. #include "stm32f0xx.h" #include "stm32f0xx_conf.h" #include "stm32f0xx_syscfg.h" #include "stdio.h" #include "HD44780.h" #include "Sht.h" // ---------------------------------------------------------------------------- // Defines // ---------------------------------------------------------------------------- #define PCF8583_ADDR ((uint16_t)(0xA0)) #define I2C_SCL GPIO_Pin_6 #define I2C_SDA GPIO_Pin_7 // Registers #define PCF8583_SEC_REG ((uint8_t)(0x02)) #define PCF8583_MIN_REG ((uint8_t)(0x03)) #define PCF8583_HOU_REG ((uint8_t)(0x04)) #define PCF8583_DAY_REG ((uint8_t)(0x05)) #define PCF8583_MON_REG ((uint8_t)(0x06)) void PCF8583_Setup(void); uint8_t PCF8583_RegRead(uint8_t reg); void PCF8583_RegWrite(uint8_t reg, uint8_t data); #define BlueLED GPIO_Pin_8 #define GreenLED GPIO_Pin_9 #define RedLED GPIO_Pin_4 #define UserBTN GPIO_Pin_0 #define ADC1_DR_Address 0x40012440 volatile uint8_t measureFlag = 0; volatile uint8_t i, j = 0; volatile char StringLoop[] = "Witaj Dolesie\n\r"; volatile uint16_t ADC_ConvertedData[4]; uint16_t TimerPeriod = 0; uint16_t Channel1Pulse = 0, Channel2Pulse = 0, Channel3Pulse = 0, Channel4Pulse = 0; uint8_t sensorsNumbers = 0; uint8_t subzero, cel, cel_fract_bits; void ADC1_CH_DMA_Config(void); void LED_Init(void); void TIMER2_Init(void); void BTN_Init(void); void Handler_Init(void); void UART1_Init(void); int main(void) { RCC_HSICmd(ENABLE); SystemInit(); SystemCoreClockUpdate(); Delay_Init(); LCD_Initialize(); LCD_Clear(); LED_Init(); BTN_Init(); TIMER2_Init(); Handler_Init(); ADC1_CH_DMA_Config(); SHT_Init(); PCF8583_Setup(); //UART1_Init(); uint16_t a, b, c, d = 0; char str_tab[50]; uint8_t sec = 0; while(1) { SHT_Reset(); if(measureFlag == 2) { SHT_GetResults(); LCD_Clear(); } /* Test DMA1 TC flag */ while((DMA_GetFlagStatus(DMA1_FLAG_TC1)) == RESET ); /* Clear DMA TC flag */ DMA_ClearFlag(DMA1_FLAG_TC1); a = ADC_ConvertedData[0]; b = ADC_ConvertedData[1]; c = ADC_ConvertedData[2]; d = ADC_ConvertedData[3]; sprintf(str_tab, "%i %.1f %i", realsht_humidity, realsht_temp, sec); LCD_GoTo(0,0); LCD_WriteText(str_tab); /* delay_ms(500); GPIO_SetBits(GPIOC, BlueLED); delay_ms(500); GPIO_ResetBits(GPIOC, BlueLED); */ sec = PCF8583_RegRead(PCF8583_SEC_REG); } return 0; } void PCF8583_Setup(void) { GPIO_InitTypeDef GPIO_InitStructure; I2C_InitTypeDef I2C_InitStructure; RCC_I2CCLKConfig(RCC_I2C1CLK_HSI); RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_1); //scl GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_1); //sda GPIO_StructInit(&GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Pin = I2C_SCL | I2C_SDA; //scl - pb6 ; sda - pb7 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); I2C_StructInit(&I2C_InitStructure); I2C_SoftwareResetCmd(I2C1); I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable; I2C_InitStructure.I2C_DigitalFilter = 0; I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_OwnAddress1 = 0; I2C_InitStructure.I2C_Timing = 0x40B22536; I2C_Init(I2C1, &I2C_InitStructure); I2C_Cmd(I2C1, ENABLE); } uint8_t PCF8583_RegRead(uint8_t reg) { uint8_t tmp = 0xFF; I2C_TransferHandling(I2C1, PCF8583_ADDR, 1, I2C_SoftEnd_Mode, I2C_GenerateF)); I2C1->ICR = I2C_ICR_STOPCF; return(tmp); } void PCF8583_RegWrite(uint8_t reg, uint8_t data) { I2C_TransferHandling(I2C1, PCF8583_ADDR, 1, I2C_Reload_Mode, I2C_GenerateF)); I2C1->ICR = I2C_ICR_STOPCF; } void EXTI0_1_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) != RESET) { j++; if(j == 1) { GPIO_SetBits(GPIOC, GreenLED); } else if(j == 2) { GPIO_ResetBits(GPIOC, GreenLED); j = 0; } measureFlag++; EXTI_ClearITPendingBit(EXTI_Line0); } } void LED_Init(void) { // configure LED output pin GPIO_InitTypeDef GPIO_LEDG; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE); GPIO_LEDG.GPIO_Pin = BlueLED | GreenLED; GPIO_LEDG.GPIO_Mode = GPIO_Mode_OUT; GPIO_LEDG.GPIO_OType = GPIO_OType_PP; GPIO_LEDG.GPIO_PuPd = GPIO_PuPd_UP; GPIO_LEDG.GPIO_Speed = GPIO_Speed_Level_1; // 2 MHz GPIO_Init(GPIOC, &GPIO_LEDG); GPIO_ResetBits(GPIOC, GreenLED); GPIO_ResetBits(GPIOC, BlueLED); } void TIMER2_Init(void) { //TimerPeriod = (SystemCoreClock / 10000 ) - 1; /* Compute CCR2 value to generate a duty cycle at 37.5% for channel 2 */ //Channel2Pulse = (uint16_t) (((uint32_t) 50 * (TimerPeriod - 1)) / 1000); /* Compute CCR3 value to generate a duty cycle at 25% for channel 3 */ //Channel3Pulse = (uint16_t) (((uint32_t) 25 * (TimerPeriod - 1)) / 1000); /* Compute CCR4 value to generate a duty cycle at 12.5% for channel 4 */ //Channel4Pulse = (uint16_t) (((uint32_t) 12 * (TimerPeriod- 1)) / 1000); //ChannelxPulse = DutyCycle * (TIM1_Period - 1) / 100 TIM_TimeBaseInitTypeDef timerInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); timerInitStructure.TIM_Prescaler = 36; timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up; timerInitStructure.TIM_Period = 20000; timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; timerInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, &timerInitStructure); TIM_Cmd(TIM2, ENABLE); TIM_OCInitTypeDef TIM_OCStruct; TIM_OCStruct.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCStruct.TIM_OutputState = TIM_OutputState_Enable; TIM_OCStruct.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCStruct.TIM_Pulse = 70; TIM_OC2Init(TIM2, &TIM_OCStruct); TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_OCStruct.TIM_Pulse = 500; TIM_OC3Init(TIM2, &TIM_OCStruct); TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_OCStruct.TIM_Pulse = 5000; TIM_OC4Init(TIM2, &TIM_OCStruct); TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_PWM_Output; GPIO_PWM_Output.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; GPIO_PWM_Output.GPIO_OType = GPIO_OType_PP; GPIO_PWM_Output.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_PWM_Output.GPIO_Mode = GPIO_Mode_AF; GPIO_PWM_Output.GPIO_Speed = GPIO_Speed_Level_3; GPIO_Init(GPIOA, &GPIO_PWM_Output); GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_2); GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_2); GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_2); } void BTN_Init(void) { // configure button input pin GPIO_InitTypeDef GPIO_BTN; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); GPIO_BTN.GPIO_Mode = GPIO_Mode_IN; GPIO_BTN.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_BTN.GPIO_Pin = UserBTN; GPIO_Init(GPIOA, &GPIO_BTN); } void Handler_Init(void) { // configure external interrupt RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); EXTI_InitTypeDef EXT_internal0; EXTI_DeInit(); EXTI_ClearITPendingBit(EXTI_Line0); EXT_internal0.EXTI_Line = EXTI_Line0 ; EXT_internal0.EXTI_Mode = EXTI_Mode_Interrupt; EXT_internal0.EXTI_Trigger = EXTI_Trigger_Rising; EXT_internal0.EXTI_LineCmd = ENABLE; EXTI_Init(&EXT_internal0); // configure NVIC for EXTI0 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0); NVIC_SetPriority(EXTI0_1_IRQn, 2); NVIC_EnableIRQ(EXTI0_1_IRQn); //Enabe EXTI0_1 IRQ //NVIC_SetPriority(TIM3_IRQn, 1); //NVIC_EnableIRQ(TIM3_IRQn); //Enabe EXTI0_1 IRQ } void ADC1_CH_DMA_Config(void) { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); ADC_InitTypeDef ADC_InitStructure; DMA_InitTypeDef DMA_InitStructure; /* ADC1 DeInit */ ADC_DeInit(ADC1); /* ADC1 Periph clock enable */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); /* DMA1 clock enable */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE); /* DMA1 Channel1 Config */ DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_Address; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedData; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = 4; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel1, &DMA_InitStructure); /* DMA1 Channel1 enable */ DMA_Cmd(DMA1_Channel1, ENABLE); /* ADC DMA request in circular mode */ ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular); /* Enable ADC_DMA */ ADC_DMACmd(ADC1, ENABLE); /* Initialize ADC structure */ ADC_StructInit(&ADC_InitStructure); /* Configure the ADC1 in continous mode withe a resolutuion equal to 12 bits */ ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward; ADC_Init(ADC1, &ADC_InitStructure); /* Convert the ADC1 Vref with 55.5 Cycles as sampling time */ ADC_ChannelConfig(ADC1, ADC_Channel_4 , ADC_SampleTime_55_5Cycles); ADC_ChannelConfig(ADC1, ADC_Channel_5 , ADC_SampleTime_55_5Cycles); ADC_ChannelConfig(ADC1, ADC_Channel_6 , ADC_SampleTime_55_5Cycles); ADC_ChannelConfig(ADC1, ADC_Channel_7 , ADC_SampleTime_55_5Cycles); /* ADC Calibration */ ADC_GetCalibrationFactor(ADC1); /* Enable ADC1 */ ADC_Cmd(ADC1, ENABLE); /* Wait the ADCEN falg */ while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN)); /* ADC1 regular Software Start Conv */ ADCBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; /* Enable GPIO clock */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); /* Enable USART clock */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); /* Connect PXx to USARTx_Tx */ GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1); /* Connect PXx to USARTx_Rx */ GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1); /* Configure USART Tx, Rx as alternate function push-pull */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); /* USART configuration */ USART_Init(USART1, &USART_InitStructure); /* Enable USART */ USART_Cmd(USART1, ENABLE); /* Enable USART1 IRQ */ NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); USART_ITConfig(USART1, USART_IT_TXE, ENABLE); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); } void USART1_IRQHandler(void) { static int tx_index = 0; static int rx_index = 0; if (USART_GetITStatus(USART1, USART_IT_TXE) != RESET) // Transmit the string in a loop { USART_SendData(USART1, StringLoop[tx_index++]); if (tx_index >= (sizeof(StringLoop) - 1)) tx_index = 0; } if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) // Received characters modify string { StringLoop[rx_index++] = USART_ReceiveData(USART1); if (rx_index >= (sizeof(StringLoop) - 1)) rx_index = 0; } } HD44780.c #include "HD44780.h" //------------------------------------- // Write half byte //------------------------------------- void _LCD_OutNibble(unsigned char nibbleToWrite) { if(nibbleToWrite & 0x01) GPIO_SetBits(LCD_GPIO, LCD_D4); else GPIO_ResetBits(LCD_GPIO, LCD_D4); if(nibbleToWrite & 0x02) GPIO_SetBits(LCD_GPIO, LCD_D5); else GPIO_ResetBits(LCD_GPIO, LCD_D5); if(nibbleToWrite & 0x04) GPIO_SetBits(LCD_GPIO, LCD_D6); else GPIO_ResetBits(LCD_GPIO, LCD_D6); if(nibbleToWrite & 0x08) GPIO_SetBits(LCD_GPIO, LCD_D7); else GPIO_ResetBits(LCD_GPIO, LCD_D7); } //------------------------------------- // Write byte //------------------------------------- void _LCD_Write(unsigned char dataToWrite) { GPIO_SetBits(LCD_GPIO, LCD_EN); _LCD_OutNibble(dataToWrite >> 4); GPIO_ResetBits(LCD_GPIO, LCD_EN); GPIO_SetBits(LCD_GPIO, LCD_EN); _LCD_OutNibble(dataToWrite); GPIO_ResetBits(LCD_GPIO, LCD_EN); delay_us(50); } //------------------------------------- // Write to command register //------------------------------------- void LCD_WriteCommand(unsigned char commandToWrite) { GPIO_ResetBits(LCD_GPIO, LCD_RS); _LCD_Write(commandToWrite); } //------------------------------------- // Write to data register //------------------------------------- void LCD_WriteData(unsigned char dataToWrite) { GPIO_SetBits(LCD_GPIO, LCD_RS); _LCD_Write(dataToWrite); } //------------------------------------- // Write text //------------------------------------- void LCD_WriteText(char * text) { while(*text) LCD_WriteData(*text++); } //------------------------------------- // Set display coordinates //------------------------------------- void LCD_GoTo(unsigned char x, unsigned char y) { LCD_WriteCommand(HD44780_DDRAM_SET | (x + (0x40 * y))); } //------------------------------------- // Clear display //------------------------------------- void LCD_Clear(void) { LCD_WriteCommand(HD44780_CLEAR); delay_ms(2); } //------------------------------------- // Set home position //------------------------------------- void LCD_Home(void) { LCD_WriteCommand(HD44780_HOME); delay_ms(2); } //------------------------------------- // Shift display left //------------------------------------- void LCD_ShiftLeft(void) { LCD_WriteCommand(HD44780_DISPLAY_CURSOR_SHIFT | HD44780_SHIFT_LEFT | HD44780_SHIFT_DISPLAY); } //------------------------------------- // Shift display right //------------------------------------- void LCD_ShiftRight(void) { LCD_WriteCommand(HD44780_DISPLAY_CURSOR_SHIFT | HD44780_SHIFT_RIGHT | HD44780_SHIFT_DISPLAY); } //------------------------------------- // Display initialization //------------------------------------- void LCD_Initialize(void) { unsigned char i; RCC_AHBPeriphClockCmd(LCD_CLK_LINE, ENABLE); GPIO_InitTypeDef GPIO_LCD_Struct; GPIO_LCD_Struct.GPIO_Pin = LCD_D4 | LCD_D5 | LCD_D6 | LCD_D7 | LCD_RS | LCD_EN; GPIO_LCD_Struct.GPIO_OType = GPIO_OType_PP; GPIO_LCD_Struct.GPIO_Speed = GPIO_Speed_Level_3; GPIO_LCD_Struct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_LCD_Struct.GPIO_Mode = GPIO_Mode_OUT; GPIO_Init(LCD_GPIO, &GPIO_LCD_Struct); delay_ms(15); GPIO_ResetBits(LCD_GPIO, LCD_RS | LCD_EN); for(i = 0; i < 3; i++) { GPIO_SetBits(LCD_GPIO, LCD_EN); _LCD_OutNibble(0x03); GPIO_ResetBits(LCD_GPIO, LCD_EN); delay_ms(5); } GPIO_SetBits(LCD_GPIO, LCD_EN); _LCD_OutNibble(0x02); GPIO_ResetBits(LCD_GPIO, LCD_EN); delay_ms(1); LCD_WriteCommand(HD44780_FUNCTION_SET | HD44780_FONT5x7 | HD44780_TWO_LINE | HD44780_4_BIT); LCD_WriteCommand(HD44780_DISPLAY_ONOFF | HD44780_DISPLAY_OFF); LCD_WriteCommand(HD44780_CLEAR); delay_ms(2); LCD_WriteCommand(HD44780_ENTRY_MODE | HD44780_EM_SHIFT_CURSOR | HD44780_EM_INCREMENT); LCD_WriteCommand(HD44780_DISPLAY_ONOFF | HD44780_DISPLAY_ON | HD44780_CURSOR_OFF | HD44780_CURSOR_NOBLINK); } //------------------------------------- // End of file //------------------------------------- HD44780.h #include "stm32f0xx.h" #include "stm32f0xx_rcc.h" #include "stm32f0xx_gpio.h" //#include "delay.h" #define LCD_GPIO GPIOA #define LCD_CLK_LINE RCC_AHBPeriph_GPIOA #define LCD_D4 GPIO_Pin_8 #define LCD_D5 GPIO_Pin_9 #define LCD_D6 GPIO_Pin_10 #define LCD_D7 GPIO_Pin_11 #define LCD_RS GPIO_Pin_12 #define LCD_EN GPIO_Pin_15 //------------------------------------------------------------------------------------------------- // HD44780 commands //------------------------------------------------------------------------------------------------- #define HD44780_CLEAR 0x01 #define HD44780_HOME 0x02 #define HD44780_ENTRY_MODE 0x04 #define HD44780_EM_SHIFT_CURSOR 0 #define HD44780_EM_SHIFT_DISPLAY 1 #define HD44780_EM_DECREMENT 0 #define HD44780_EM_INCREMENT 2 #define HD44780_DISPLAY_ONOFF 0x08 #define HD44780_DISPLAY_OFF 0 #define HD44780_DISPLAY_ON 4 #define HD44780_CURSOR_OFF 0 #define HD44780_CURSOR_ON 2 #define HD44780_CURSOR_NOBLINK 0 #define HD44780_CURSOR_BLINK 1 #define HD44780_DISPLAY_CURSOR_SHIFT 0x10 #define HD44780_SHIFT_CURSOR 0 #define HD44780_SHIFT_DISPLAY 8 #define HD44780_SHIFT_LEFT 0 #define HD44780_SHIFT_RIGHT 4 #define HD44780_FUNCTION_SET 0x20 #define HD44780_FONT5x7 0 #define HD44780_FONT5x10 4 #define HD44780_ONE_LINE 0 #define HD44780_TWO_LINE 8 #define HD44780_4_BIT 0 #define HD44780_8_BIT 16 #define HD44780_CGRAM_SET 0x40 #define HD44780_DDRAM_SET 0x80 //------------------------------------------------------------------------------------------------- // Functions declarations //------------------------------------------------------------------------------------------------- void LCD_WriteCommand(unsigned char); void LCD_WriteData(unsigned char); void LCD_WriteText(char *); void LCD_GoTo(unsigned char, unsigned char); void LCD_Clear(void); void LCD_Home(void); void LCD_ShiftLeft(void); void LCD_ShiftRight(void); void LCD_Initialize(void); //------------------------------------------------------------------------------------------------- // End of file //------------------------------------------------------------------------------------------------- SHTxx.c //---------------------------------------------------------------------------------- // // Sensirion SHTxx Humidity Sensor Library // // Library for using the SHT1x humidity and temperature // sensors from Sensirion (http://www.sensirion.com). // Based on Sensirion application note "Sample Code SHTxx". // // To use: // - supply 5V to SHTxx (constants in calculations assume 5V, see ShtCalculate() and SHTxx datasheet) // - connect the clock pin of the SHTxx to pin B1 (see Sht.h to change) // - connect the data pin of the SHTxx to pin B2 (see Sht.h to change) // // - call ShtInit() to initialize pins, call when the processor starts // - call ShtMeasure(MEASURE_TEMP) and ShtMeasure(MEASURE_HUMIDITY) to make the measurements // - call ShtCalculate() to convert measurements to real-world units // // - call ShtReadStatus() and ShtWriteStatus() to modify the status register // // // ToDo: // - verify checksum digits sent from SHTxx // - implement soft-reset // - handle 12/8-bit temp/humidity readings // // History: // 2003-Jul-03 BL - Created // //---------------------------------------------------------------------------------- #include "sht.h" //#include "delay.h" void SHT_EnableData(void) { GPIO_InitTypeDef GPIO_SHT_Struct; GPIO_SHT_Struct.GPIO_Pin = SHT_DATA; GPIO_SHT_Struct.GPIO_OType = GPIO_OType_PP; GPIO_SHT_Struct.GPIO_Speed = GPIO_Speed_Level_3; GPIO_SHT_Struct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_SHT_Struct.GPIO_Mode = GPIO_Mode_OUT; GPIO_Init(SHT_GPIO, &GPIO_SHT_Struct); } void SHT_DisableData(void) { GPIO_InitTypeDef GPIO_SHT_Struct; GPIO_SHT_Struct.GPIO_Pin = SHT_DATA; GPIO_SHT_Struct.GPIO_OType = GPIO_OType_PP; GPIO_SHT_Struct.GPIO_Speed = GPIO_Speed_Level_3; GPIO_SHT_Struct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_SHT_Struct.GPIO_Mode = GPIO_Mode_IN; GPIO_Init(SHT_GPIO, &GPIO_SHT_Struct); GPIO_SetBits(SHT_GPIO, SHT_DATA); } void SHT_DataHigh(void) { GPIO_SetBits(SHT_GPIO, SHT_DATA); } void SHT_DataLow(void) { GPIO_ResetBits(SHT_GPIO, SHT_DATA); } void SHT_ClockHigh(void) { GPIO_SetBits(SHT_GPIO, SHT_CLOCK); } void SHT_ClockLow(void) { GPIO_ResetBits(SHT_GPIO, SHT_CLOCK); } void SHT_Init(void) { RCC_AHBPeriphClockCmd(SHT_GPIO_CLK, ENABLE); GPIO_InitTypeDef GPIO_SHT_Struct; GPIO_SHT_Struct.GPIO_Pin = SHT_CLOCK; GPIO_SHT_Struct.GPIO_OType = GPIO_OType_PP; GPIO_SHT_Struct.GPIO_Speed = GPIO_Speed_Level_3; GPIO_SHT_Struct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_SHT_Struct.GPIO_Mode = GPIO_Mode_OUT; GPIO_Init(SHT_GPIO, &GPIO_SHT_Struct); GPIO_ResetBits(SHT_GPIO, SHT_CLOCK); SHT_DisableData(); } //---------------------------------------------------------------------------------- // generates a transmission start // _____ ________ // DATA: |_______| // ___ ___ // SCK : ___| |___| |______ // //---------------------------------------------------------------------------------- void SHT_TransmissionStart(void) { SHT_EnableData(); SHT_DataHigh(); SHT_ClockLow(); delay_us(5); delay_us(5); SHT_ClockHigh(); delay_us(5); SHT_DataLow(); delay_us(5); SHT_ClockLow(); delay_us(10); SHT_ClockHigh(); delay_us(5); SHT_DataHigh(); delay_us(5); SHT_ClockLow(); } //---------------------------------------------------------------------------------- // communication reset: DATA-line=1 and at least 9 SCK cycles followed by transstart // _____________________________________________________ ________ // DATA: |_______| // _ _ _ _ _ _ _ _ _ ___ ___ // SCK : __| |__| |__| |__| |__| |__| |__| |__| |__| |______| |___| |______ //---------------------------------------------------------------------------------- void SHT_Reset(void) { unsigned char i; SHT_EnableData(); SHT_DataHigh(); SHT_ClockLow(); for(i = 0; i < 10; i++) //9 SCK cycles { SHT_ClockHigh(); } SHT_TransmissionStart(); //transmission start } //---------------------------------------------------------------------------------- // reads a byte form the Sensibus and gives an acknowledge in case of "ack=1" //---------------------------------------------------------------------------------- char SHT_ReadByte(void) { unsigned char i, val=0; SHT_DisableData(); // Read in 8 bits, LSB first for (i = 0x80; i > 0; i /= 2) { SHT_ClockHigh(); delay_us(5); if(GPIO_ReadInputDataBit(SHT_GPIO, SHT_DATA)) val = (val | i); //read bit SHT_ClockLow(); } // Send ACK SHT_EnableData(); SHT_DataLow(); SHT_ClockHigh(); delay_us(5); SHT_ClockLow(); SHT_DisableData(); return val; } //---------------------------------------------------------------------------------- // Writes a byte on the Sensibus and checks the acknowledge. // Returns 0 if the successful //---------------------------------------------------------------------------------- char SHT_WriteByte(unsigned char value) { unsigned char i; unsigned char error = 0; // Write each bit one at a time, LSB first SHT_EnableData(); for (i = 0x80; i >0; i /= 2) { if (i & value) SHT_DataHigh(); else SHT_DataLow(); SHT_ClockHigh(); delay_us(5); delay_us(5); SHT_ClockLow(); } SHT_DisableData(); // Read ACK SHT_ClockHigh(); delay_us(5); error = GPIO_ReadInputDataBit(SHT_GPIO, SHT_DATA); SHT_ClockLow(); return error; //error=1 in case of no acknowledge } //---------------------------------------------------------------------------------- // Read humidity or temperature from the sensor. // Returns the value in ticks. Use sht_calc() to convert to real world units. // Returns 0xFFFF if the measurement failed //---------------------------------------------------------------------------------- int SHT_Measure(unsigned char mode) { unsigned int temp = 0xFFFF; unsigned char c; // Signal start of communications SHT_TransmissionStart(); // Request measurement SHT_WriteByte(mode); // Sensor lowers the data line when measurement // is complete. Wait up to 2 seconds for this. for (c = 0; c < 20; c++) { if(! GPIO_ReadInputDataBit(SHT_GPIO, SHT_DATA)) break; delay_ms(15); } // Read the measurement if(! GPIO_ReadInputDataBit(SHT_GPIO, SHT_DATA)) { temp = SHT_ReadByte(); temp = temp << 8; temp += SHT_ReadByte(); } return temp; } //---------------------------------------------------------------------------------------- // Calculates tempurature in ^C and humidity in %RH (temperature compensated) // sht_measure() returns temp and humidity in ticks. Use this function to convert // to compensated values in real world units. // // This function returns integers with 2 assumed decimal places. For example 2550 // means 25.50. This is to avoid including the floating point math library. // // input : humi [Ticks] (12 bit) // temp [Ticks] (14 bit) // output: humi [%RH] (2 fixed decimals) // temp [°C] (2 fixed decimals) //---------------------------------------------------------------------------------------- void SHT_Calculate(int *p_temperature, int *p_humidity) { const long D1x100 = -40 * 100; // for 5V power const long D2x100 = 0.01 * 100; // for 14bit temp const long C1x100 = -4 * 100; // for 12bit humidity const long C2x10000 = 0.0405 * 10000; // for 12bit humidity const long C3x10000000 = -0.0000028 * 10000000; // for 12bit humidity const long T1x100000 = 0.01 * 100000; // for 12bit humidity const long T2x100000 = 0.00008 * 100000; // for 12bit humidity long t = *p_temperature; // temperatere in ticks from sensor long rh = *p_humidity; // humidity in ticks from sensor long t_C; // temperature in celcius: 2 fixed decimals long rh_lin; // relative humidity: 2 fixed decimals long rh_true; // temp compensated humidity: 2 fixed decimals t_C = D1x100 + D2x100*t; // calculate tempurature in celcius from ticks rh_lin = (C3x10000000*rh*rh)/100000 + (C2x10000*rh)/100 + C1x100; rh_true = ((t_C-(25*100)) * (T1x100000 + T2x100000*rh))/100000 + rh_lin; if(rh_true > 10000) rh_true = 10000; //cut if the value is outside of if(rh_true < 10) rh_true = 10; //the physical possible range *p_temperature = (int)t_C; //return temperature [^C] *p_humidity = (int)rh_true; //return humidity[%RH] } //---------------------------------------------------------------------------------- // Reads the status register //---------------------------------------------------------------------------------- char SHT_ReadStatusReg(unsigned char *p_value) { unsigned char error = 0; SHT_TransmissionStart(); //transmission start error = SHT_WriteByte(STATUS_REG_R); //send command to sensor *p_value = SHT_ReadByte(); //read status register (8-bit) return error; //error=1 in case of no response form the sensor } //---------------------------------------------------------------------------------- // Writes the status register . Note this library only supports the default // 14 bit temp and 12 bit humidity readings. //---------------------------------------------------------------------------------- char SHT_WriteStatusReg(unsigned char value) { unsigned char error = 0; SHT_TransmissionStart(); //transmission start error += SHT_WriteByte(STATUS_REG_W); //send command to sensor error += SHT_WriteByte(value); //send value of status register return error; //error>=1 in case of no response form the sensor } void SHT_GetResults(void) { temp_ticks = SHT_Measure(SHT_TEMPERATURE); humidity_ticks = SHT_Measure(SHT_HUMIDITY); temp_sht = (int)temp_ticks; humidity_sht = (int)humidity_ticks; SHT_Calculate(&temp_sht, &humidity_sht); //konwersja z binarki na odpowiednią wartość liczb rzeczywistych realsht_temp = (float)temp_sht/100; //przeliczenie temperatury na wartość z przecinkiem realsht_humidity = (int)humidity_sht/100; //przeliczenie wilgotności na wartość z przecinkiem } char SHT_SoftReset(void) { unsigned char error = 0; SHT_TransmissionStart(); //reset communication error += SHT_WriteByte(RESET); //send RESET-command to sensor return error; //error=1 in case of no response form the sensor } SHTxx.h //---------------------------------------------------------------------------------- // // Sensirion SHT1x Humidity Sensor Library // //---------------------------------------------------------------------------------- #include "stm32f0xx.h" #include "stm32f0xx_rcc.h" #include "stm32f0xx_gpio.h" #include "stdbool.h" #ifndef __sht_h #define __sht_h #define SHT_TEMPERATURE 0x03 // Measure temp - for ShtMeasure #define SHT_HUMIDITY 0x05 // Measure humidity - for ShtMeasure #define SHT_GPIO GPIOB #define SHT_DATA GPIO_Pin_13 #define SHT_CLOCK GPIO_Pin_14 #define SHT_GPIO_CLK RCC_AHBPeriph_GPIOB #define STATUS_REG_W 0x06 // Command to read status register #define STATUS_REG_R 0x07 // Command to write status register #define RESET 0x1E // Command for soft reset (not currently used) int humidity_sht; int temp_sht; unsigned int temp_ticks; unsigned int humidity_ticks; int realsht_humidity; float realsht_temp; void SHT_Init(void); void SHT_Reset(void); int SHT_Measure(unsigned char mode); void SHT_Calculate(int *p_temperature, int *p_humidity); char SHT_ReadStatusReg(unsigned char *p_value); char SHT_WriteStatusReg(unsigned char value); void SHT_GetResults(void); char SHT_SoftReset(void); #endif Faktycznie - przyznaję się, że PCFka również zwraca kod BCD...szybko napisałem dwie funkcje do konwersji kodu bcd i decymalnego. Może też komuś się przyda: //------------------------------------- // Convert int decimal value to BCD value //------------------------------------- int DecToBcd(int data) { return ((data/10) << 4) + (data % 10); } //------------------------------------- // Convert BCD value to int decimal value //------------------------------------- int BcdToDec(int data) { return (10*(data>>4) + (data & 0x0F)); } Najmocniej dziękuję za pomoc !! [ Dodano: 15-02-2015, 20:47 ] Mam jeszcze jedno ciekawe pytanie, otóż zacząłem robić sobie obsługę pamięci EEPROM 24C64, i co ciekawe komunikacja jest, ale...problem jest taki, że, gdy na magistrali mam oba urządzenia, tzn. pamięć eeprom i pcfke, to w pętli głównej while(1) wykonuje się najpierw odczyt sekund z pcfki, następnie odczyt jakiejś strony pamięci i gdy pętla "wraca" od nowa i dochodzi do odczytu sekund z pcfki, to zostaje w nieszczęsnej pętli while. W takim razie pojawia się pytanie - jaka jest "procedura" obsługi wielu urządzeń na i2c w stm32 ? Czy muszę np. włączać i wyłączać magistralę ? Coś resetować ? sec = PCF8583_RegRead(PCF8583_SEC_REG); delay_ms(10); page_value = EEPROM_ReadByte(0);
  3. Dzięki, coś ruszyło. Jest komunikacja po I2C. Odczyt jest nieco dziwny, bo odczytuje mi sekundy np. 0, 1...9, nagle 16, 17, 21, itd. Zlicza w ogóle do 89...a przecież sekund mamy 59 Wiem, że PCFka ma kod BCD, ale to chyba obowiązuje przy zapisie a nie odczycie ? Nie przypominam sobie abym w jakichkolwiek projektach z tym scalakiem coś robił przy odczycie. Po prostu odczytywałem bajt i tyle.
  4. Niestety bez zmian, to samo. Przeczytałem też gdzieś, że I2C w STMach potrafi się zawiesić po skonfigurowaniu taktowania lub po konfiguracji jego pinów. Tzn. w momencie ustawienia wyprowadzeń I2C jako Alternate Function i wybór pola GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; ze struktury powoduje, że piny przełączają się ze stanu OD w stan wysokiej impedancji co I2C wykrywa jako ERROR i pętla While zawsze stoi. Solucją tego problemu jest zresetowanie magistrali I2C i robię to po włączeniu zegara oraz konfiguracji pinów: I2C_SoftwareResetCmd(I2C1); Ale to również nic nie daje...
  5. Witam wszystkich serdecznie, Chciałbym prosić o pomoc w analizie błędów inicjalizacji i komunikacji z zegarem RTC, który wszyscy znają. Chciałbym przedstawić kod, który nie działa - opisać objawy i spytać o parę rzeczy, które mnie nurtują. A więc do rzeczy. Najpierw Kod: #define PCF8583_ADDR ((uint16_t)(0x50)) #define I2C_SCL GPIO_Pin_6 #define I2C_SDA GPIO_Pin_7 // Registers #define PCF8583_SEC_REG ((uint8_t)(0x02)) #define PCF8583_MIN_REG ((uint8_t)(0x03)) #define PCF8583_HOU_REG ((uint8_t)(0x04)) #define PCF8583_DAY_REG ((uint8_t)(0x05)) #define PCF8583_MON_REG ((uint8_t)(0x06)) void PCF8583_Setup(void); uint8_t PCF8583_RegRead(uint8_t reg); void PCF8583_RegWrite(uint8_t reg, uint8_t data); void PCF8583_Setup(void) { GPIO_InitTypeDef GPIO_InitStructure; I2C_InitTypeDef I2C_InitStructure; RCC_I2CCLKConfig(RCC_I2C1CLK_HSI); RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_1); //scl GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_1); //sda GPIO_StructInit(&GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Pin = I2C_SCL | I2C_SDA; //scl - pb6 ; sda - pb7 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); I2C_DeInit(I2C1); I2C_StructInit(&I2C_InitStructure); I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable; I2C_InitStructure.I2C_DigitalFilter = 0; I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_OwnAddress1 = 0; I2C_InitStructure.I2C_Timing = 0x40B22536; I2C_Init(I2C1, &I2C_InitStructure); I2C_Cmd(I2C1, ENABLE); } uint8_t PCF8583_RegRead(uint8_t reg) { // kopia kodu z AVR //TWI(); /* I2C_GenerateSTART(I2C1, ENABLE); I2C_SendData(I2C1,(uint32_t) 0x50); //address write I2C_SendData(I2C1, reg); //register to read I2C_GenerateSTART(I2C1, ENABLE); I2C_SendData(I2C1,(uint32_t) 0x50); //address read tmp = I2C_ReceiveData(I2C1); I2C_GenerateSTOP(I2C1, ENABLE); */ uint8_t tmp = 0xFF; I2C_TransferHandling(I2C1, PCF8583_ADDR, 1, I2C_SoftEnd_Mode, I2C_GenerateF)); I2C1->ICR = I2C_ICR_STOPCF; return(tmp); } void PCF8583_RegWrite(uint8_t reg, uint8_t data) { I2C_TransferHandling(I2C1, PCF8583_ADDR, 1, I2C_Reload_Mode, I2C_GenerateF)); I2C1->ICR = I2C_ICR_STOPCF; } Adres urządzenia zgodnie z datasheetem - 0b101000 A0 RW. U mnie pin A0 zwarty jest do masy, więc adres wygląda następująco: 0b1010000 - 0x50. STM32 z tego co rozumiem wymaga adresów 7 bitowych a najmłodszy bit R/W ustawia sam np. przy wywołaniu funkcji: - I2C_TransferHandling Dobrze rozumiem ? Po drugie, czy adresowanie 0x50 jest wystarczające, czy mam używać jakiś operacji bitowych ? Nie mogę znaleźć o tym informacji. Po trzecie, czy jest jakaś różnica, jeśli tak to jaka w inicjalizacji GPIO oraz I2C ? Że najpierw np. zegar RCC peryferiów, potem struktura GPIO, potem funkcje alternatywne, dalej jakiś DeInit, itd. Po czwarte, o co chodzi w polu struktury I2C_InitStructure.I2C_Timing = 0x40B22536; ? Tzn. rozumiem, że chodzi o częstotliwość zegara I2C, ale jak oblicza się tę wartość ? Zaraz ktoś odeśle mnie do skomplikowanego manuala Cortexa - tak, czytam, analizuje, mam ciągle otwarty, ale nie dla wszystkich jest on łatwy i czytelny. Teraz do rzeczy: problem jest taki, że gdy wywołuję w main.c funkcję odczytu rejestru z RTC, czyli: sec = PCF8583_RegRead(PCF8583_SEC_REG); // rejestr 0x02 gdzie zmienna sec to uint8_t, program zawiesza się i zostaje w pierwszej pętli while tejże funkcji -> while(!(I2C1->ISR & I2C_ISR_TXIS)); z której już nigdy nie wychodzi. Jak mam rozumieć ten błąd ? Czy w tym wypadku w ogóle generowane jest cokolwiek ? Czy ten kod w ogóle jest dobry ? Napisałem go na bazie exampli od ST. Rezystory podciągające 10kΩ, długość lini to ~ 15cm więc nie ma to znaczenia. Układ RTC sprawny, wielokrotnie używany. Będę wdzięczny za jakiekolwiek wskazówki z I2C do STMów, bo totalnie tego nie ogarniam. Dzięki z góry !
  6. Koledzy proszę, wręcz błagam Was o pomoc...rozwaliłem swojego discovery F0. Tzn. chciałem pobawić się I2C, skapnąłem się, że mam na liniach I2C1 lcd hd44780 więc przestawiłem jego kabelki na portA...niestety również na SWD Pin PA13 i PA14. Nie skapnąłem się, co za destrukcyjne działanie wykonuję. Rozwaliłem chyba bootloader w płytce, nie mogę wcale łączyć się przez CoCoox z discovery, programować, debugować. Wyskakuje ten sam błąd co opisywany w temacie. Poprzez STM32 ST-LINK Utility normalnie się łączy i mogę programować. Co mam zrobić aby naprawić ten problem i czy w ogóle da się to jeszcze wykonać? Czy muszę kupować nowy zestaw discovery ? Totalnie nie wiem jak mam teraz to naprawić, wpadłem w panikę bo już coś tam zaczęło mi działać na stm...dopiero się ich uczę 2 miesiące, ogarnąłem sobie podstawowe peryferia, timery, pwm, przerwania zewnętrzne, adc z dma, usart, systick, lcd na hd44780, czujnik wilgotności i temperatury sht11 a tu nagle taki błąd..Kombinowałem sposoby "naprawy" opisane w temacie niestety żaden nie pomaga. Znalazłem gdzieś info, że skasowałem "bootloader code" co to jest ? Jak to przywrócić? Grabo: niestety Twój pomysł nijak się ma do realiów i naprawdę nie działa.
×
×
  • Utwórz nowe...